{"version":3,"file":"agent-session-auto-handoff.test.d.ts","sourceRoot":"","sources":["../../src/core/agent-session-auto-handoff.test.ts"],"names":[],"mappings":"","sourcesContent":["import { describe, expect, it, vi } from \"vitest\";\nimport { AgentSession } from \"./agent-session.ts\";\nimport type { ExtensionRunner } from \"./extensions/runner.ts\";\nimport type { ResolvedCommand } from \"./extensions/types.ts\";\nimport { SettingsManager } from \"./settings-manager.ts\";\n\nfunction createMockSession({\n\tenabled,\n\tthreshold = 128_000,\n\ttokens,\n\tmode = \"tui\",\n\tcommand,\n}: {\n\tenabled: boolean;\n\tthreshold?: number;\n\ttokens: number | null;\n\tmode?: \"tui\" | \"print\" | \"rpc\";\n\tcommand?: ResolvedCommand | undefined;\n}) {\n\tconst settings = SettingsManager.inMemory({\n\t\tautoHandoff: { enabled, thresholdTokens: threshold },\n\t});\n\n\tconst getCommand = vi.fn(() => command);\n\tconst handler = command?.handler ?? vi.fn();\n\tconst createCommandContext = vi.fn(() => ({ mock: \"ctx\" }) as unknown as never);\n\tconst emitError = vi.fn();\n\n\tconst extensionRunner = {\n\t\tgetCommand,\n\t\tcreateCommandContext,\n\t\temitError,\n\t\temit: vi.fn(),\n\t} as unknown as ExtensionRunner;\n\n\tconst resourceLoader = {\n\t\tgetExtensions: () => ({ extensions: [], errors: [], runtime: { flagValues: new Map(), pendingProviderRegistrations: [], pendingNativeProviderRegistrations: [], assertActive: () => {} } }),\n\t\tgetSkills: () => ({ skills: [], diagnostics: [] }),\n\t\tgetPrompts: () => ({ prompts: [], diagnostics: [] }),\n\t\tgetThemes: () => ({ themes: [], diagnostics: [] }),\n\t\tgetAgents: () => ({ agents: [], diagnostics: [] }),\n\t\tgetAgentsFiles: () => ({ agentsFiles: [] }),\n\t\tgetSystemPrompt: () => undefined,\n\t\tgetAppendSystemPrompt: () => [],\n\t\tgetExtensionDiagnostics: () => [],\n\t\textendResources: () => {},\n\t\treload: vi.fn(),\n\t};\n\n\tconst modelRegistry = {\n\t\tregisterProvider: vi.fn(),\n\t\tunregisterProvider: vi.fn(),\n\t\tgetProvider: vi.fn(),\n\t\tgetProviders: vi.fn(() => []),\n\t\tgetModels: vi.fn(() => []),\n\t};\n\n\tconst agent = {\n\t\tstate: { messages: [], tools: [] },\n\t\tsubscribe: vi.fn(() => () => {}),\n\t};\n\n\tconst session = new AgentSession({\n\t\tagent: agent as unknown as never,\n\t\tsessionManager: { getBranch: () => [] } as unknown as never,\n\t\tsettingsManager: settings,\n\t\tcwd: \"/tmp\",\n\t\tresourceLoader: resourceLoader as unknown as never,\n\t\tmodelRuntime: modelRegistry as unknown as never,\n\t});\n\n\t(session as any)._extensionRunner = extensionRunner;\n\t(session as any)._extensionMode = mode;\n\t(session as any).getContextUsage = () =>\n\t\ttokens === null ? null : { tokens, contextWindow: 200_000, percent: tokens / 200_000 };\n\n\treturn { session, getCommand, createCommandContext, handler, emitError, settings };\n}\n\ndescribe(\"AgentSession auto handoff\", () => {\n\tit(\"invokes handoff-new once when enabled and threshold reached in tui mode\", async () => {\n\t\tconst command = { handler: vi.fn() } as unknown as ResolvedCommand;\n\t\tconst { session, getCommand, createCommandContext, emitError } = createMockSession({\n\t\t\tenabled: true,\n\t\t\ttokens: 128_000,\n\t\t\tcommand,\n\t\t});\n\n\t\tawait (session as any)._checkAutoHandoff();\n\t\tawait (session as any)._checkAutoHandoff();\n\n\t\texpect(getCommand).toHaveBeenCalledWith(\"handoff-new\");\n\t\texpect(createCommandContext).toHaveBeenCalled();\n\t\texpect(command.handler).toHaveBeenCalledTimes(1);\n\t\texpect(command.handler).toHaveBeenCalledWith(\"\", expect.anything());\n\t\texpect(emitError).not.toHaveBeenCalled();\n\t});\n\n\tit(\"dispatches auto handoff via _emitAgentSettled in tui mode\", async () => {\n\t\tconst command = { handler: vi.fn() } as unknown as ResolvedCommand;\n\t\tconst { session } = createMockSession({ enabled: true, tokens: 200_000, command });\n\t\tconst events: string[] = [];\n\t\tsession.subscribe((event) => {\n\t\t\tif (event.type === \"agent_settled\") events.push(event.type);\n\t\t});\n\n\t\tawait (session as any)._emitAgentSettled();\n\n\t\texpect(events).toEqual([\"agent_settled\"]);\n\t\texpect(command.handler).toHaveBeenCalledTimes(1);\n\t});\n\n\tit(\"_emitAgentSettled does not auto-handoff in non-tui mode\", async () => {\n\t\tconst command = { handler: vi.fn() } as unknown as ResolvedCommand;\n\t\tconst { session } = createMockSession({\n\t\t\tenabled: true,\n\t\t\ttokens: 200_000,\n\t\t\tmode: \"print\",\n\t\t\tcommand,\n\t\t});\n\t\tconst events: string[] = [];\n\t\tsession.subscribe((event) => {\n\t\t\tif (event.type === \"agent_settled\") events.push(event.type);\n\t\t});\n\n\t\tawait (session as any)._emitAgentSettled();\n\n\t\texpect(events).toEqual([\"agent_settled\"]);\n\t\texpect(command.handler).not.toHaveBeenCalled();\n\t});\n\n\tit(\"does nothing when disabled\", async () => {\n\t\tconst command = { handler: vi.fn() } as unknown as ResolvedCommand;\n\t\tconst { session, getCommand } = createMockSession({ enabled: false, tokens: 200_000, command });\n\n\t\tawait (session as any)._checkAutoHandoff();\n\n\t\texpect(getCommand).not.toHaveBeenCalled();\n\t});\n\n\tit(\"does nothing when tokens are below threshold\", async () => {\n\t\tconst command = { handler: vi.fn() } as unknown as ResolvedCommand;\n\t\tconst { session, getCommand } = createMockSession({ enabled: true, tokens: 127_999, command });\n\n\t\tawait (session as any)._checkAutoHandoff();\n\n\t\texpect(getCommand).not.toHaveBeenCalled();\n\t});\n\n\tit(\"does nothing when tokens are null\", async () => {\n\t\tconst command = { handler: vi.fn() } as unknown as ResolvedCommand;\n\t\tconst { session, getCommand } = createMockSession({ enabled: true, tokens: null, command });\n\n\t\tawait (session as any)._checkAutoHandoff();\n\n\t\texpect(getCommand).not.toHaveBeenCalled();\n\t});\n\n\tit(\"does nothing in non-tui mode\", async () => {\n\t\tconst command = { handler: vi.fn() } as unknown as ResolvedCommand;\n\t\tconst { session, getCommand } = createMockSession({\n\t\t\tenabled: true,\n\t\t\ttokens: 200_000,\n\t\t\tmode: \"rpc\",\n\t\t\tcommand,\n\t\t});\n\n\t\tawait (session as any)._checkAutoHandoff();\n\n\t\texpect(getCommand).not.toHaveBeenCalled();\n\t});\n\n\tit(\"does nothing when handoff-new command is not registered\", async () => {\n\t\tconst { session, getCommand } = createMockSession({ enabled: true, tokens: 200_000, command: undefined });\n\n\t\tawait (session as any)._checkAutoHandoff();\n\n\t\texpect(getCommand).toHaveBeenCalledWith(\"handoff-new\");\n\t});\n\n\tit(\"emits error when handoff-new throws but does not rethrow\", async () => {\n\t\tconst command = {\n\t\t\thandler: vi.fn(() => Promise.reject(new Error(\"boom\"))),\n\t\t} as unknown as ResolvedCommand;\n\t\tconst { session, emitError } = createMockSession({ enabled: true, tokens: 200_000, command });\n\n\t\tawait expect((session as any)._checkAutoHandoff()).resolves.toBeUndefined();\n\n\t\texpect(emitError).toHaveBeenCalledWith({\n\t\t\textensionPath: \"command:handoff-new\",\n\t\t\tevent: \"auto-handoff\",\n\t\t\terror: \"boom\",\n\t\t});\n\t});\n\n\tit(\"resets triggered flag when tokens drop below threshold\", async () => {\n\t\tconst command = { handler: vi.fn() } as unknown as ResolvedCommand;\n\t\tconst { session, getCommand } = createMockSession({ enabled: true, tokens: 200_000, command });\n\n\t\tawait (session as any)._checkAutoHandoff();\n\t\texpect(command.handler).toHaveBeenCalledTimes(1);\n\n\t\t// Simulate tokens dropping below threshold (e.g. compaction/new session)\n\t\t(session as any).getContextUsage = () => ({ tokens: 1000, contextWindow: 200_000, percent: 0.5 });\n\t\tawait (session as any)._checkAutoHandoff();\n\t\texpect(command.handler).toHaveBeenCalledTimes(1); // still once\n\n\t\t// Tokens rise above threshold again\n\t\t(session as any).getContextUsage = () => ({ tokens: 200_000, contextWindow: 200_000, percent: 100 });\n\t\tawait (session as any)._checkAutoHandoff();\n\t\texpect(command.handler).toHaveBeenCalledTimes(2);\n\t});\n});\n"]}