{"version":3,"file":"agent-session-brief-only.test.d.ts","sourceRoot":"","sources":["../../src/core/agent-session-brief-only.test.ts"],"names":[],"mappings":"","sourcesContent":["import { mkdtempSync, rmSync } from \"node:fs\";\nimport { tmpdir } from \"node:os\";\nimport { join } from \"node:path\";\nimport { Agent } from \"@apholdings/jensen-agent-core\";\nimport { afterEach, describe, expect, it, vi } from \"vitest\";\nimport { AgentSession } from \"./agent-session.js\";\nimport { AuthStorage } from \"./auth-storage.js\";\nimport { BTW_CUSTOM_TYPE } from \"./btw-command.js\";\nimport { ModelRegistry } from \"./model-registry.js\";\nimport { DefaultResourceLoader } from \"./resource-loader.js\";\nimport { SessionManager } from \"./session-manager.js\";\nimport { SettingsManager } from \"./settings-manager.js\";\n\nfunction createHarness(options?: { briefOnly?: boolean }) {\n\tconst rootDir = mkdtempSync(join(tmpdir(), \"jensen-brief-only-\"));\n\tconst agentDir = join(rootDir, \"agent\");\n\tconst cwd = join(rootDir, \"repo\");\n\n\tconst authStorage = AuthStorage.create(join(agentDir, \"auth.json\"));\n\tconst modelRegistry = new ModelRegistry(authStorage, undefined);\n\tconst settingsManager = SettingsManager.create(cwd, agentDir);\n\tconst sessionManager = SessionManager.create(cwd);\n\tconst resourceLoader = new DefaultResourceLoader({ cwd, agentDir, settingsManager });\n\tconst model = {\n\t\tid: \"test-model\",\n\t\tprovider: \"test-provider\",\n\t\tapi: \"openai-chat\",\n\t\tbaseUrl: \"url\",\n\t\treasoning: false,\n\t\tinput: [\"text\"] as Array<\"text\" | \"image\">,\n\t\tcost: { input: 0, output: 0, cacheRead: 0, cacheWrite: 0 },\n\t\tcontextWindow: 8000,\n\t\tmaxTokens: 4000,\n\t\tname: \"Test Model\",\n\t};\n\n\tmodelRegistry.registerProvider(\"test-provider\", {\n\t\tbaseUrl: \"url\",\n\t\tapiKey: \"dummy-key\",\n\t\tapi: \"openai-chat\",\n\t\tmodels: [model],\n\t});\n\n\tconst agent = new Agent({\n\t\tinitialState: {\n\t\t\tsystemPrompt: \"\",\n\t\t\tmodel,\n\t\t\tthinkingLevel: \"off\",\n\t\t\ttools: [],\n\t\t},\n\t});\n\n\tconst session = new AgentSession({\n\t\tagent,\n\t\tsessionManager,\n\t\tsettingsManager,\n\t\tcwd,\n\t\tresourceLoader,\n\t\tmodelRegistry,\n\t\tbriefOnly: options?.briefOnly,\n\t});\n\n\treturn {\n\t\trootDir,\n\t\tsession,\n\t\tcleanup: () => rmSync(rootDir, { recursive: true, force: true }),\n\t};\n}\n\ndescribe(\"AgentSession brief-only mode\", () => {\n\tconst cleanups: Array<() => void> = [];\n\n\tafterEach(() => {\n\t\twhile (cleanups.length > 0) {\n\t\t\tcleanups.pop()?.();\n\t\t}\n\t});\n\n\tit(\"leaves the default system prompt unchanged when brief-only is disabled\", () => {\n\t\tconst harness = createHarness();\n\t\tcleanups.push(harness.cleanup);\n\n\t\texpect(harness.session.briefOnly).toBe(false);\n\t\texpect(harness.session.systemPrompt).not.toContain(\"Brief-only mode is enabled for this session.\");\n\t});\n\n\tit(\"adds the brief-only contract to the session system prompt when enabled\", () => {\n\t\tconst harness = createHarness();\n\t\tcleanups.push(harness.cleanup);\n\n\t\tharness.session.setBriefOnly(true);\n\n\t\texpect(harness.session.briefOnly).toBe(true);\n\t\texpect(harness.session.systemPrompt).toContain(\"Brief-only mode is enabled for this session.\");\n\t\texpect(harness.session.systemPrompt).toContain(\"Suppress ordinary assistant prose.\");\n\t\texpect(harness.session.systemPrompt).toContain(\"Do not hide tool activity, approvals, warnings, errors\");\n\t});\n\n\tit(\"still routes explicit brief output through sendUserMessage semantics when brief-only is enabled\", async () => {\n\t\tconst harness = createHarness({ briefOnly: true });\n\t\tcleanups.push(harness.cleanup);\n\n\t\tconst promptSpy = vi.spyOn(harness.session, \"prompt\").mockResolvedValue(undefined);\n\n\t\tawait harness.session.sendUserMessage(\"brief update\", { deliverAs: \"followUp\" });\n\n\t\texpect(promptSpy).toHaveBeenCalledWith(\"brief update\", {\n\t\t\texpandPromptTemplates: false,\n\t\t\tstreamingBehavior: \"followUp\",\n\t\t\timages: undefined,\n\t\t\tsource: \"extension\",\n\t\t});\n\t});\n\n\tit(\"queues /btw notes as runtime-only next-turn context and clears them after injection\", async () => {\n\t\tconst harness = createHarness();\n\t\tcleanups.push(harness.cleanup);\n\n\t\tharness.session.queueByTheWay(\"keep the slice minimal\");\n\t\tharness.session.queueByTheWay(\"do not broaden into /steer\");\n\n\t\texpect(harness.session.getPendingByTheWayNotes()).toEqual([\n\t\t\t\"keep the slice minimal\",\n\t\t\t\"do not broaden into /steer\",\n\t\t]);\n\t\texpect(\n\t\t\tharness.session.sessionManager\n\t\t\t\t.getEntries()\n\t\t\t\t.some((entry) => entry.type === \"custom_message\" && entry.customType === BTW_CUSTOM_TYPE),\n\t\t).toBe(false);\n\n\t\tconst promptSpy = vi.spyOn(harness.session.agent, \"prompt\").mockResolvedValue(undefined);\n\t\tawait harness.session.prompt(\"continue with the main task\");\n\n\t\texpect(promptSpy).toHaveBeenCalledTimes(1);\n\t\texpect(promptSpy.mock.calls[0]?.[0]).toMatchObject([\n\t\t\t{\n\t\t\t\trole: \"user\",\n\t\t\t\tcontent: [{ type: \"text\", text: \"continue with the main task\" }],\n\t\t\t},\n\t\t\t{\n\t\t\t\trole: \"custom\",\n\t\t\t\tcustomType: BTW_CUSTOM_TYPE,\n\t\t\t\tdisplay: false,\n\t\t\t\tdetails: { note: \"keep the slice minimal\" },\n\t\t\t},\n\t\t\t{\n\t\t\t\trole: \"custom\",\n\t\t\t\tcustomType: BTW_CUSTOM_TYPE,\n\t\t\t\tdisplay: false,\n\t\t\t\tdetails: { note: \"do not broaden into /steer\" },\n\t\t\t},\n\t\t]);\n\t\texpect(harness.session.getPendingByTheWayNotes()).toEqual([]);\n\t});\n\n\tit(\"keeps brief-only mode runtime-only across new session instances\", () => {\n\t\tconst enabledHarness = createHarness({ briefOnly: true });\n\t\tcleanups.push(enabledHarness.cleanup);\n\t\texpect(enabledHarness.session.briefOnly).toBe(true);\n\n\t\tconst freshHarness = createHarness();\n\t\tcleanups.push(freshHarness.cleanup);\n\t\texpect(freshHarness.session.briefOnly).toBe(false);\n\t});\n});\n"]}