{"version":3,"file":"agent-session-apply-ultraplan.test.d.ts","sourceRoot":"","sources":["../../src/core/agent-session-apply-ultraplan.test.ts"],"names":[],"mappings":"","sourcesContent":["import { Agent } from \"@apholdings/jensen-agent-core\";\nimport { Type } from \"@sinclair/typebox\";\nimport { describe, expect, it } from \"vitest\";\nimport { AgentSession } from \"./agent-session.js\";\nimport { AuthStorage } from \"./auth-storage.js\";\nimport type { ToolDefinition } from \"./extensions/index.js\";\nimport { SESSION_TODOS_CUSTOM_TYPE } from \"./memory.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\";\nimport { SESSION_ULTRAPLAN_CUSTOM_TYPE } from \"./ultraplan.js\";\n\nfunction createSession(options?: { sessionManager?: SessionManager; customTools?: ToolDefinition[] }): AgentSession {\n\tconst settingsManager = SettingsManager.inMemory();\n\tconst resourceLoader = new DefaultResourceLoader({\n\t\tcwd: \"/tmp/project\",\n\t\tagentDir: \"/tmp/agent\",\n\t\tsettingsManager,\n\t});\n\tconst authStorage = AuthStorage.create(\"/tmp/agent/auth.json\");\n\tconst modelRegistry = new ModelRegistry(authStorage);\n\tconst agent = new Agent({\n\t\tinitialState: {\n\t\t\tsystemPrompt: \"\",\n\t\t\tthinkingLevel: \"off\",\n\t\t\ttools: [],\n\t\t},\n\t});\n\n\treturn new AgentSession({\n\t\tagent,\n\t\tsessionManager: options?.sessionManager ?? SessionManager.inMemory(\"/tmp/project\"),\n\t\tsettingsManager,\n\t\tcwd: \"/tmp/project\",\n\t\tresourceLoader,\n\t\tmodelRegistry,\n\t\tcustomTools: options?.customTools,\n\t});\n}\n\nfunction createSubagentTool(\n\thandler: (params: { agent: string; task: string; agentScope?: string }) => string,\n): ToolDefinition {\n\treturn {\n\t\tname: \"subagent\",\n\t\tlabel: \"subagent\",\n\t\tdescription: \"Fake subagent tool for Ultraplan apply tests\",\n\t\tparameters: Type.Object({}),\n\t\texecute: async (_toolCallId, params) => {\n\t\t\tconst typedParams = params as unknown as { agent: string; task: string; agentScope?: string };\n\t\t\treturn {\n\t\t\t\tcontent: [{ type: \"text\", text: handler(typedParams) }],\n\t\t\t\tdetails: undefined,\n\t\t\t};\n\t\t},\n\t};\n}\n\ndescribe(\"AgentSession.applyUltraplan()\", () => {\n\tit(\"applies the latest persisted plan artifact into persisted session todo state\", async () => {\n\t\tconst sessionManager = SessionManager.inMemory(\"/tmp/project\");\n\t\tconst writer = createSession({\n\t\t\tsessionManager,\n\t\t\tcustomTools: [\n\t\t\t\tcreateSubagentTool(() =>\n\t\t\t\t\tJSON.stringify({\n\t\t\t\t\t\tobjective: \"Apply latest plan\",\n\t\t\t\t\t\tassumptions: [],\n\t\t\t\t\t\tconstraints: [\"Do not auto-execute\"],\n\t\t\t\t\t\tphases: [{ title: \"Prepare\", steps: [\"Inspect the repo\", \"Write the todo state\"] }],\n\t\t\t\t\t\trisks: [],\n\t\t\t\t\t\trecommendedExecutionOrder: [\"Plan\", \"Apply\", \"Execute later\"],\n\t\t\t\t\t\tactionableNextSteps: [\"Inspect the repo\", \"Write the todo state\"],\n\t\t\t\t\t}),\n\t\t\t\t),\n\t\t\t],\n\t\t});\n\n\t\tawait writer.runUltraplan(\"Apply latest plan\");\n\n\t\tconst applier = createSession({ sessionManager });\n\t\tconst result = applier.applyUltraplan();\n\t\tconst reloaded = createSession({ sessionManager });\n\n\t\texpect(result.applied).toMatchObject([\n\t\t\t{ content: \"Inspect the repo\", activeForm: \"Inspect the repo\", status: \"pending\" },\n\t\t\t{ content: \"Write the todo state\", activeForm: \"Write the todo state\", status: \"pending\" },\n\t\t]);\n\t\texpect(reloaded.getTodos()).toMatchObject(result.applied);\n\t\texpect(result.displayText).toContain(\"Applied 2 Ultraplan step(s)\");\n\t\texpect(result.displayText).toContain(\"session todo state\");\n\t\texpect(result.displayText).toContain(\"Apply did not start execution\");\n\t});\n\n\tit(\"uses the latest persisted Ultraplan artifact when multiple plans exist on the branch\", async () => {\n\t\tconst sessionManager = SessionManager.inMemory(\"/tmp/project\");\n\t\tconst outputs = [\n\t\t\tJSON.stringify({\n\t\t\t\tobjective: \"Older plan\",\n\t\t\t\tassumptions: [],\n\t\t\t\tconstraints: [],\n\t\t\t\tphases: [{ title: \"Old\", steps: [\"Use the outdated step\"] }],\n\t\t\t\trisks: [],\n\t\t\t\trecommendedExecutionOrder: [],\n\t\t\t\tactionableNextSteps: [\"Use the outdated step\"],\n\t\t\t}),\n\t\t\tJSON.stringify({\n\t\t\t\tobjective: \"Latest plan\",\n\t\t\t\tassumptions: [],\n\t\t\t\tconstraints: [],\n\t\t\t\tphases: [{ title: \"Latest\", steps: [\"Use the latest step\"] }],\n\t\t\t\trisks: [],\n\t\t\t\trecommendedExecutionOrder: [],\n\t\t\t\tactionableNextSteps: [\"Use the latest step\"],\n\t\t\t}),\n\t\t];\n\t\tconst writer = createSession({\n\t\t\tsessionManager,\n\t\t\tcustomTools: [\n\t\t\t\tcreateSubagentTool(() => {\n\t\t\t\t\tconst output = outputs.shift();\n\t\t\t\t\tif (!output) {\n\t\t\t\t\t\tthrow new Error(\"Unexpected extra planner call\");\n\t\t\t\t\t}\n\t\t\t\t\treturn output;\n\t\t\t\t}),\n\t\t\t],\n\t\t});\n\n\t\tawait writer.runUltraplan(\"Older plan\");\n\t\tawait writer.runUltraplan(\"Latest plan\");\n\n\t\tconst applier = createSession({ sessionManager });\n\t\tconst result = applier.applyUltraplan();\n\n\t\texpect(result.applied).toMatchObject([\n\t\t\t{ content: \"Use the latest step\", activeForm: \"Use the latest step\", status: \"pending\" },\n\t\t]);\n\t\texpect(applier.getLatestUltraplanPlan()?.objective).toBe(\"Latest plan\");\n\t});\n\n\tit(\"falls back to first-phase steps when actionable next steps are empty\", async () => {\n\t\tconst sessionManager = SessionManager.inMemory(\"/tmp/project\");\n\t\tconst writer = createSession({\n\t\t\tsessionManager,\n\t\t\tcustomTools: [\n\t\t\t\tcreateSubagentTool(() =>\n\t\t\t\t\tJSON.stringify({\n\t\t\t\t\t\tobjective: \"Fallback plan\",\n\t\t\t\t\t\tassumptions: [],\n\t\t\t\t\t\tconstraints: [],\n\t\t\t\t\t\tphases: [{ title: \"First phase\", steps: [\"Phase step one\", \"Phase step two\"] }],\n\t\t\t\t\t\trisks: [],\n\t\t\t\t\t\trecommendedExecutionOrder: [],\n\t\t\t\t\t\tactionableNextSteps: [],\n\t\t\t\t\t}),\n\t\t\t\t),\n\t\t\t],\n\t\t});\n\n\t\tawait writer.runUltraplan(\"Fallback plan\");\n\n\t\tconst applier = createSession({ sessionManager });\n\t\tconst result = applier.applyUltraplan();\n\n\t\texpect(result.applied).toMatchObject([\n\t\t\t{ content: \"Phase step one\", activeForm: \"Phase step one\", status: \"pending\" },\n\t\t\t{ content: \"Phase step two\", activeForm: \"Phase step two\", status: \"pending\" },\n\t\t]);\n\t\texpect(result.displayText).toContain(\"Source: first phase steps\");\n\t});\n\n\tit(\"preserves existing todo state, avoids duplicate todos, and keeps the plan artifact intact\", async () => {\n\t\tconst sessionManager = SessionManager.inMemory(\"/tmp/project\");\n\t\tsessionManager.appendCustomEntry(SESSION_TODOS_CUSTOM_TYPE, [\n\t\t\t{ content: \"Existing task\", activeForm: \"Existing task\", status: \"in_progress\" },\n\t\t\t{ content: \"Shared step\", activeForm: \"Shared step\", status: \"completed\" },\n\t\t]);\n\t\tconst writer = createSession({\n\t\t\tsessionManager,\n\t\t\tcustomTools: [\n\t\t\t\tcreateSubagentTool(() =>\n\t\t\t\t\tJSON.stringify({\n\t\t\t\t\t\tobjective: \"Preserve state\",\n\t\t\t\t\t\tassumptions: [],\n\t\t\t\t\t\tconstraints: [],\n\t\t\t\t\t\tphases: [{ title: \"Apply\", steps: [\"Shared step\", \"New step\"] }],\n\t\t\t\t\t\trisks: [],\n\t\t\t\t\t\trecommendedExecutionOrder: [],\n\t\t\t\t\t\tactionableNextSteps: [\"Shared step\", \"New step\"],\n\t\t\t\t\t}),\n\t\t\t\t),\n\t\t\t],\n\t\t});\n\n\t\tawait writer.runUltraplan(\"Preserve state\");\n\n\t\tconst applier = createSession({ sessionManager });\n\t\tconst beforeApplyArtifact = applier.getLatestUltraplanPlan();\n\t\tconst result = applier.applyUltraplan();\n\t\tconst afterApplyArtifact = applier.getLatestUltraplanPlan();\n\n\t\texpect(result.applied).toMatchObject([{ content: \"New step\", activeForm: \"New step\", status: \"pending\" }]);\n\t\texpect(applier.getTodos()).toMatchObject([\n\t\t\t{ content: \"Existing task\", activeForm: \"Existing task\", status: \"in_progress\" },\n\t\t\t{ content: \"Shared step\", activeForm: \"Shared step\", status: \"completed\" },\n\t\t\t{ content: \"New step\", activeForm: \"New step\", status: \"pending\" },\n\t\t]);\n\t\texpect(afterApplyArtifact).toEqual(beforeApplyArtifact);\n\t\texpect(\n\t\t\tsessionManager\n\t\t\t\t.getBranch()\n\t\t\t\t.some((entry) => entry.type === \"custom\" && entry.customType === SESSION_ULTRAPLAN_CUSTOM_TYPE),\n\t\t).toBe(true);\n\t});\n\n\tit(\"does not auto-execute or misrepresent delegated work as applied state\", async () => {\n\t\tconst sessionManager = SessionManager.inMemory(\"/tmp/project\");\n\t\tconst writer = createSession({\n\t\t\tsessionManager,\n\t\t\tcustomTools: [\n\t\t\t\tcreateSubagentTool(() =>\n\t\t\t\t\tJSON.stringify({\n\t\t\t\t\t\tobjective: \"Apply only\",\n\t\t\t\t\t\tassumptions: [],\n\t\t\t\t\t\tconstraints: [\"No execution\"],\n\t\t\t\t\t\tphases: [{ title: \"Plan\", steps: [\"Keep execution separate\"] }],\n\t\t\t\t\t\trisks: [],\n\t\t\t\t\t\trecommendedExecutionOrder: [],\n\t\t\t\t\t\tactionableNextSteps: [\"Keep execution separate\"],\n\t\t\t\t\t}),\n\t\t\t\t),\n\t\t\t],\n\t\t});\n\n\t\tawait writer.runUltraplan(\"Apply only\");\n\n\t\tconst applier = createSession({ sessionManager });\n\t\tconst result = applier.applyUltraplan();\n\n\t\texpect(applier.getDelegatedWorkSummary().total).toBe(0);\n\t\texpect(applier.state.messages).toHaveLength(0);\n\t\texpect(result.displayText).not.toContain(\"running\");\n\t\texpect(result.displayText).not.toContain(\"executing\");\n\t\texpect(result.displayText).toContain(\"Apply did not start execution\");\n\t});\n\n\tit(\"fails honestly when no persisted plan exists\", () => {\n\t\tconst session = createSession();\n\n\t\texpect(() => session.applyUltraplan()).toThrow(\n\t\t\t\"No persisted Ultraplan plan found. Run /ultraplan <objective> first.\",\n\t\t);\n\t});\n});\n"]}