summaryrefslogtreecommitdiffhomepage
path: root/packages/transport-http/src/app.test.ts
diff options
context:
space:
mode:
authorAdam Malczewski <[email protected]>2026-06-24 00:08:47 +0900
committerAdam Malczewski <[email protected]>2026-06-24 00:08:47 +0900
commitd225ea4bd5f95d39a910704fe45acdf847c953fa (patch)
treeec0a33665087bb34844b19955ab6fa74c39e3656 /packages/transport-http/src/app.test.ts
parent674853d87d54dba1cd83c4e51fce5411602f4d5d (diff)
downloaddispatch-d225ea4bd5f95d39a910704fe45acdf847c953fa.tar.gz
dispatch-d225ea4bd5f95d39a910704fe45acdf847c953fa.zip
feat(system-prompt): wire into turn flow + compaction + API routes
session-orchestrator: - Wire systemPromptService as optional dep (lazy via host.getService) - Regular turn: construct on first turn (new conversation), get on subsequent turns, set on providerOpts.systemPrompt (cache-safe) - Compaction: construct (fresh resolve) + append COMPACTION_SYSTEM_PROMPT - 12 new tests (construct/get/service-unavailable/compaction) transport-http: - GET /system-prompt (returns template or DEFAULT_TEMPLATE) - PUT /system-prompt (validate + setTemplate, 503 when unavailable) - GET /system-prompt/variables (static catalog, always available) - 6 new tests system-prompt service: added getTemplate/setTemplate to interface + impl. 1396 vitest pass. typecheck + biome clean.
Diffstat (limited to 'packages/transport-http/src/app.test.ts')
-rw-r--r--packages/transport-http/src/app.test.ts147
1 files changed, 147 insertions, 0 deletions
diff --git a/packages/transport-http/src/app.test.ts b/packages/transport-http/src/app.test.ts
index 0b840db..2a4b451 100644
--- a/packages/transport-http/src/app.test.ts
+++ b/packages/transport-http/src/app.test.ts
@@ -10,11 +10,13 @@ import type {
StoredChunk,
TurnMetrics,
} from "@dispatch/kernel";
+import { DEFAULT_TEMPLATE } from "@dispatch/system-prompt";
import { createThroughputStore, dayKeyOf } from "@dispatch/throughput-store";
import type {
DeleteWorkspaceResponse,
QueuedMessage,
QueueResponse,
+ SystemPromptVariable,
ThroughputResponse,
WorkspaceListResponse,
WorkspaceResponse,
@@ -28,6 +30,7 @@ import type {
CredentialStore,
LspService,
SessionOrchestrator,
+ SystemPromptService,
WarmService,
} from "./seam.js";
import { conversationOpened } from "./seam.js";
@@ -381,6 +384,39 @@ function createCapturingLspService(
};
}
+function createFakeSystemPromptService(
+ template: string = "custom template",
+): SystemPromptService & {
+ readonly setTemplateCalls: readonly string[];
+ readonly getTemplateCalls: number;
+} {
+ const setCalls: string[] = [];
+ let getTemplateCount = 0;
+ let currentTemplate = template;
+ return {
+ get setTemplateCalls() {
+ return setCalls;
+ },
+ get getTemplateCalls() {
+ return getTemplateCount;
+ },
+ async construct() {
+ return currentTemplate;
+ },
+ async get() {
+ return currentTemplate;
+ },
+ async getTemplate() {
+ getTemplateCount++;
+ return currentTemplate;
+ },
+ async setTemplate(t) {
+ setCalls.push(t);
+ currentTemplate = t;
+ },
+ };
+}
+
const noopLogger = createFakeLogger();
describe("GET /health", () => {
@@ -3249,3 +3285,114 @@ it("GET /conversations/:id/lsp uses effective cwd", async () => {
};
expect(body.cwd).toBe("/effective");
});
+
+describe("GET /system-prompt", () => {
+ it("returns stored template", async () => {
+ const service = createFakeSystemPromptService("custom template");
+ const app = createApp({
+ conversationStore: createFakeConversationStore(),
+ orchestrator: createFakeOrchestrator([]),
+ credentialStore: createFakeCredentialStore([]),
+ systemPromptService: service,
+ logger: noopLogger,
+ });
+ const res = await app.request("/system-prompt");
+ expect(res.status).toBe(200);
+ const body = (await res.json()) as { template: string };
+ expect(body.template).toBe("custom template");
+ expect(service.getTemplateCalls).toBe(1);
+ });
+
+ it("returns default when service unavailable", async () => {
+ const app = createApp({
+ conversationStore: createFakeConversationStore(),
+ orchestrator: createFakeOrchestrator([]),
+ credentialStore: createFakeCredentialStore([]),
+ logger: noopLogger,
+ });
+ const res = await app.request("/system-prompt");
+ expect(res.status).toBe(200);
+ const body = (await res.json()) as { template: string };
+ expect(body.template).toBe(DEFAULT_TEMPLATE);
+ });
+});
+
+describe("PUT /system-prompt", () => {
+ it("sets template", async () => {
+ const service = createFakeSystemPromptService();
+ const app = createApp({
+ conversationStore: createFakeConversationStore(),
+ orchestrator: createFakeOrchestrator([]),
+ credentialStore: createFakeCredentialStore([]),
+ systemPromptService: service,
+ logger: noopLogger,
+ });
+ const res = await app.request("/system-prompt", {
+ method: "PUT",
+ headers: { "Content-Type": "application/json" },
+ body: JSON.stringify({ template: "new" }),
+ });
+ expect(res.status).toBe(200);
+ const body = (await res.json()) as { template: string };
+ expect(body.template).toBe("new");
+ expect(service.setTemplateCalls).toEqual(["new"]);
+ });
+
+ it("missing template → 400", async () => {
+ const service = createFakeSystemPromptService();
+ const app = createApp({
+ conversationStore: createFakeConversationStore(),
+ orchestrator: createFakeOrchestrator([]),
+ credentialStore: createFakeCredentialStore([]),
+ systemPromptService: service,
+ logger: noopLogger,
+ });
+ const res = await app.request("/system-prompt", {
+ method: "PUT",
+ headers: { "Content-Type": "application/json" },
+ body: JSON.stringify({}),
+ });
+ expect(res.status).toBe(400);
+ expect(service.setTemplateCalls).toEqual([]);
+ });
+
+ it("service unavailable → 503", async () => {
+ const app = createApp({
+ conversationStore: createFakeConversationStore(),
+ orchestrator: createFakeOrchestrator([]),
+ credentialStore: createFakeCredentialStore([]),
+ logger: noopLogger,
+ });
+ const res = await app.request("/system-prompt", {
+ method: "PUT",
+ headers: { "Content-Type": "application/json" },
+ body: JSON.stringify({ template: "new" }),
+ });
+ expect(res.status).toBe(503);
+ const body = (await res.json()) as { error: string };
+ expect(body.error).toBe("System prompt service not available");
+ });
+});
+
+describe("GET /system-prompt/variables", () => {
+ it("returns catalog", async () => {
+ const app = createApp({
+ conversationStore: createFakeConversationStore(),
+ orchestrator: createFakeOrchestrator([]),
+ credentialStore: createFakeCredentialStore([]),
+ logger: noopLogger,
+ });
+ const res = await app.request("/system-prompt/variables");
+ expect(res.status).toBe(200);
+ const body = (await res.json()) as { variables: readonly SystemPromptVariable[] };
+ expect(Array.isArray(body.variables)).toBe(true);
+ // Contains at least system:time, prompt:cwd, and a dynamic file:<path>.
+ const hasSystemTime = body.variables.some((v) => v.type === "system" && v.name === "time");
+ const hasPromptCwd = body.variables.some((v) => v.type === "prompt" && v.name === "cwd");
+ const fileEntry = body.variables.find((v) => v.type === "file");
+ expect(hasSystemTime).toBe(true);
+ expect(hasPromptCwd).toBe(true);
+ expect(fileEntry).toBeDefined();
+ expect(fileEntry?.dynamic).toBe(true);
+ });
+});