@gethmy/mcp 2.4.6 → 2.5.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,260 @@
1
+ /**
2
+ * Verification harness for MCP tool dispatch. Card #182.
3
+ *
4
+ * Pins the contract between Harmony's MCP server and any MCP client (Claude
5
+ * Code, Codex, Cursor, etc.). If the dispatch shape drifts, agents lose tools
6
+ * silently, so this is the floor:
7
+ *
8
+ * 1. TOOLS registry is well-formed (shape, names, schemas).
9
+ * 2. ListTools handler emits the same set TOOLS declares.
10
+ * 3. CallTool handler routes valid names and isErrors unknown ones.
11
+ * 4. ListResources / ReadResource cover the published URIs.
12
+ * 5. Tool ↔ skill name spaces are disjoint.
13
+ *
14
+ * Run with: bun test packages/mcp-server/src/__tests__/tool-dispatch.test.ts
15
+ */
16
+
17
+ import { beforeAll, describe, expect, test } from "bun:test";
18
+ import {
19
+ CallToolRequestSchema,
20
+ ListResourcesRequestSchema,
21
+ ListToolsRequestSchema,
22
+ ReadResourceRequestSchema,
23
+ } from "@modelcontextprotocol/sdk/types.js";
24
+ import {
25
+ RESOURCES,
26
+ registerHandlers,
27
+ TOOLS,
28
+ type ToolDeps,
29
+ } from "../server.js";
30
+ import { SKILL_DEFINITIONS } from "../skills.js";
31
+
32
+ const TOOL_NAMES = Object.keys(TOOLS);
33
+
34
+ // ── Fake transport ────────────────────────────────────────────────────────
35
+
36
+ type Schema = unknown;
37
+ type Handler = (req: unknown) => Promise<unknown>;
38
+
39
+ class FakeServer {
40
+ handlers = new Map<Schema, Handler>();
41
+ setRequestHandler(schema: Schema, handler: Handler): void {
42
+ this.handlers.set(schema, handler);
43
+ }
44
+ call<T = unknown>(schema: Schema, request: unknown): Promise<T> {
45
+ const handler = this.handlers.get(schema);
46
+ if (!handler) throw new Error("Handler not registered");
47
+ return handler(request) as Promise<T>;
48
+ }
49
+ }
50
+
51
+ function makeDeps(overrides: Partial<ToolDeps> = {}): ToolDeps {
52
+ return {
53
+ getClient: () => ({}) as never,
54
+ isConfigured: () => true,
55
+ getActiveProjectId: () => "11111111-1111-1111-1111-111111111111",
56
+ getActiveWorkspaceId: () => "22222222-2222-2222-2222-222222222222",
57
+ setActiveProject: () => {},
58
+ setActiveWorkspace: () => {},
59
+ getApiUrl: () => "http://localhost",
60
+ getMemoryDir: () => null,
61
+ getUserEmail: () => null,
62
+ saveConfig: () => {},
63
+ resetClient: () => {},
64
+ ...overrides,
65
+ };
66
+ }
67
+
68
+ // ── Static registry shape ────────────────────────────────────────────────
69
+
70
+ describe("TOOLS registry", () => {
71
+ test("is non-empty", () => {
72
+ expect(TOOL_NAMES.length).toBeGreaterThan(0);
73
+ });
74
+
75
+ test("every name is unique", () => {
76
+ expect(new Set(TOOL_NAMES).size).toBe(TOOL_NAMES.length);
77
+ });
78
+
79
+ test.each(TOOL_NAMES)("%s is namespaced under harmony_", (name) => {
80
+ expect(name).toMatch(/^harmony_[a-z0-9_]+$/);
81
+ });
82
+
83
+ test.each(TOOL_NAMES)("%s has description and inputSchema", (name) => {
84
+ const tool = (
85
+ TOOLS as Record<string, { description?: unknown; inputSchema?: unknown }>
86
+ )[name];
87
+ expect(typeof tool.description).toBe("string");
88
+ expect((tool.description as string).length).toBeGreaterThan(0);
89
+ expect(typeof tool.inputSchema).toBe("object");
90
+ expect(tool.inputSchema).not.toBeNull();
91
+ });
92
+
93
+ test.each(TOOL_NAMES)("%s inputSchema is a JSON Schema object", (name) => {
94
+ const schema = (
95
+ TOOLS as Record<
96
+ string,
97
+ { inputSchema: { type?: string; properties?: unknown } }
98
+ >
99
+ )[name].inputSchema;
100
+ expect(schema.type).toBe("object");
101
+ if ("properties" in schema && schema.properties !== undefined) {
102
+ expect(typeof schema.properties).toBe("object");
103
+ }
104
+ });
105
+ });
106
+
107
+ // ── Skill ↔ tool boundary ────────────────────────────────────────────────
108
+
109
+ describe("skill ↔ tool namespace boundary", () => {
110
+ test("skill names and tool names are disjoint", () => {
111
+ const skillNames = new Set(Object.keys(SKILL_DEFINITIONS));
112
+ const collisions = TOOL_NAMES.filter((name) => skillNames.has(name));
113
+ expect(collisions).toEqual([]);
114
+ });
115
+ });
116
+
117
+ // ── Dispatch round-trip ──────────────────────────────────────────────────
118
+
119
+ describe("registerHandlers — ListTools", () => {
120
+ let server: FakeServer;
121
+
122
+ beforeAll(() => {
123
+ server = new FakeServer();
124
+ registerHandlers(server as never, makeDeps());
125
+ });
126
+
127
+ test("registers all four request handlers", () => {
128
+ expect(server.handlers.has(ListToolsRequestSchema)).toBe(true);
129
+ expect(server.handlers.has(CallToolRequestSchema)).toBe(true);
130
+ expect(server.handlers.has(ListResourcesRequestSchema)).toBe(true);
131
+ expect(server.handlers.has(ReadResourceRequestSchema)).toBe(true);
132
+ });
133
+
134
+ test("emits one entry per TOOLS row, with name+description+inputSchema", async () => {
135
+ const result = await server.call<{
136
+ tools: Array<{ name: string; description: string; inputSchema: unknown }>;
137
+ }>(ListToolsRequestSchema, { method: "tools/list" });
138
+
139
+ expect(result.tools.length).toBe(TOOL_NAMES.length);
140
+
141
+ const emittedNames = result.tools.map((t) => t.name).sort();
142
+ expect(emittedNames).toEqual([...TOOL_NAMES].sort());
143
+
144
+ for (const tool of result.tools) {
145
+ expect(typeof tool.name).toBe("string");
146
+ expect(typeof tool.description).toBe("string");
147
+ expect(typeof tool.inputSchema).toBe("object");
148
+ }
149
+ });
150
+ });
151
+
152
+ describe("registerHandlers — CallTool", () => {
153
+ let server: FakeServer;
154
+ let workspaceId: string | null;
155
+ let projectId: string | null;
156
+
157
+ beforeAll(() => {
158
+ server = new FakeServer();
159
+ workspaceId = "22222222-2222-2222-2222-222222222222";
160
+ projectId = "11111111-1111-1111-1111-111111111111";
161
+ const deps = makeDeps({
162
+ getActiveWorkspaceId: () => workspaceId,
163
+ getActiveProjectId: () => projectId,
164
+ });
165
+ registerHandlers(server as never, deps);
166
+ });
167
+
168
+ test("routes harmony_get_context and returns active ids", async () => {
169
+ const result = await server.call<{
170
+ content: Array<{ type: string; text: string }>;
171
+ isError?: boolean;
172
+ }>(CallToolRequestSchema, {
173
+ method: "tools/call",
174
+ params: { name: "harmony_get_context", arguments: {} },
175
+ });
176
+
177
+ expect(result.isError).toBeUndefined();
178
+ expect(result.content[0].type).toBe("text");
179
+ const payload = JSON.parse(result.content[0].text);
180
+ expect(payload.success).toBe(true);
181
+ expect(payload.context.activeWorkspaceId).toBe(workspaceId);
182
+ expect(payload.context.activeProjectId).toBe(projectId);
183
+ });
184
+
185
+ test("unknown tool name returns isError without throwing", async () => {
186
+ const result = await server.call<{
187
+ content: Array<{ type: string; text: string }>;
188
+ isError?: boolean;
189
+ }>(CallToolRequestSchema, {
190
+ method: "tools/call",
191
+ params: { name: "harmony_does_not_exist", arguments: {} },
192
+ });
193
+
194
+ expect(result.isError).toBe(true);
195
+ expect(result.content[0].text.toLowerCase()).toContain("error");
196
+ });
197
+
198
+ test("not-configured rejects authenticated tools", async () => {
199
+ const unconfigured = new FakeServer();
200
+ registerHandlers(
201
+ unconfigured as never,
202
+ makeDeps({ isConfigured: () => false }),
203
+ );
204
+
205
+ const result = await unconfigured.call<{
206
+ content: Array<{ type: string; text: string }>;
207
+ isError?: boolean;
208
+ }>(CallToolRequestSchema, {
209
+ method: "tools/call",
210
+ params: { name: "harmony_get_context", arguments: {} },
211
+ });
212
+
213
+ expect(result.isError).toBe(true);
214
+ expect(result.content[0].text).toContain("Not configured");
215
+ });
216
+ });
217
+
218
+ // ── Resources ────────────────────────────────────────────────────────────
219
+
220
+ describe("registerHandlers — Resources", () => {
221
+ let server: FakeServer;
222
+
223
+ beforeAll(() => {
224
+ server = new FakeServer();
225
+ registerHandlers(server as never, makeDeps());
226
+ });
227
+
228
+ test("ListResources mirrors the static RESOURCES array", async () => {
229
+ const result = await server.call<{ resources: typeof RESOURCES }>(
230
+ ListResourcesRequestSchema,
231
+ { method: "resources/list" },
232
+ );
233
+ expect(result.resources).toEqual(RESOURCES);
234
+ });
235
+
236
+ test("ReadResource serves harmony://context as JSON", async () => {
237
+ const result = await server.call<{
238
+ contents: Array<{ uri: string; mimeType: string; text: string }>;
239
+ }>(ReadResourceRequestSchema, {
240
+ method: "resources/read",
241
+ params: { uri: "harmony://context" },
242
+ });
243
+
244
+ expect(result.contents[0].uri).toBe("harmony://context");
245
+ expect(result.contents[0].mimeType).toBe("application/json");
246
+ const parsed = JSON.parse(result.contents[0].text);
247
+ expect(parsed).toHaveProperty("configured");
248
+ expect(parsed).toHaveProperty("activeWorkspaceId");
249
+ expect(parsed).toHaveProperty("activeProjectId");
250
+ });
251
+
252
+ test("ReadResource on unknown URI throws", async () => {
253
+ await expect(
254
+ server.call(ReadResourceRequestSchema, {
255
+ method: "resources/read",
256
+ params: { uri: "harmony://nope" },
257
+ }),
258
+ ).rejects.toThrow(/Unknown resource/);
259
+ });
260
+ });
package/src/api-client.ts CHANGED
@@ -1,3 +1,4 @@
1
+ import { getDisplayLinkType } from "@harmony/shared";
1
2
  import { getApiKey, getApiUrl } from "./config.js";
2
3
 
3
4
  export interface ApiResponse<T = unknown> {
@@ -149,6 +150,10 @@ export class HarmonyApiClient {
149
150
  this.apiKey = apiKey;
150
151
  }
151
152
 
153
+ getApiKey(): string {
154
+ return this.apiKey;
155
+ }
156
+
152
157
  private async request<T>(
153
158
  method: string,
154
159
  path: string,
@@ -638,34 +643,6 @@ export class HarmonyApiClient {
638
643
  return this.request("GET", `/cards/${cardId}/agent-context${query}`);
639
644
  }
640
645
 
641
- // ============ AGENT PERFORMANCE PROFILES ============
642
-
643
- async getAgentProfile(
644
- workspaceId: string,
645
- agentIdentifier: string,
646
- ): Promise<{ profile: unknown }> {
647
- const params = new URLSearchParams({
648
- workspace_id: workspaceId,
649
- agent_identifier: agentIdentifier,
650
- });
651
- return this.request("GET", `/agent-profiles?${params.toString()}`);
652
- }
653
-
654
- async listAgentProfiles(
655
- workspaceId: string,
656
- ): Promise<{ profiles: unknown[] }> {
657
- const params = new URLSearchParams({ workspace_id: workspaceId });
658
- return this.request("GET", `/agent-profiles?${params.toString()}`);
659
- }
660
-
661
- async refreshAgentProfiles(
662
- workspaceId: string,
663
- ): Promise<{ refreshed: boolean }> {
664
- return this.request("POST", "/agent-profiles/refresh", {
665
- workspace_id: workspaceId,
666
- });
667
- }
668
-
669
646
  // ============ MEMORY OPERATIONS ============
670
647
 
671
648
  async createMemoryEntity(data: {
@@ -678,6 +655,7 @@ export class HarmonyApiClient {
678
655
  content: string;
679
656
  metadata?: Record<string, unknown>;
680
657
  confidence?: number;
658
+ importance?: number;
681
659
  tags?: string[];
682
660
  agent_identifier?: string;
683
661
  }): Promise<{ entity: unknown; warnings?: string[] }> {
@@ -695,6 +673,7 @@ export class HarmonyApiClient {
695
673
  q?: string;
696
674
  limit?: number;
697
675
  offset?: number;
676
+ include_superseded?: boolean;
698
677
  }): Promise<{ entities: unknown[]; count: number }> {
699
678
  const params = new URLSearchParams();
700
679
  params.set("workspace_id", options.workspace_id);
@@ -710,6 +689,7 @@ export class HarmonyApiClient {
710
689
  if (options.limit !== undefined) params.set("limit", String(options.limit));
711
690
  if (options.offset !== undefined)
712
691
  params.set("offset", String(options.offset));
692
+ if (options.include_superseded) params.set("include_superseded", "true");
713
693
  return this.request("GET", `/memory/entities?${params.toString()}`);
714
694
  }
715
695
 
@@ -728,6 +708,10 @@ export class HarmonyApiClient {
728
708
  scope?: string;
729
709
  type?: string;
730
710
  memory_tier?: string;
711
+ // AGP lifecycle fields. Backend may not yet whitelist these — extra keys
712
+ // are dropped server-side, leaving the call as a no-op for those fields.
713
+ superseded_by?: string | null;
714
+ version?: number;
731
715
  },
732
716
  ): Promise<{ entity: unknown; warnings?: string[] }> {
733
717
  return this.request("PUT", `/memory/entities/${entityId}`, updates);
@@ -1063,6 +1047,44 @@ export class HarmonyApiClient {
1063
1047
  return this.request("POST", "/api-keys", { name });
1064
1048
  }
1065
1049
 
1050
+ // ============ PROMPT HISTORY (AGP P2) ============
1051
+
1052
+ async recordPromptHistory(data: {
1053
+ cardId: string;
1054
+ generatedPrompt: string;
1055
+ variant: "analysis" | "draft" | "execute";
1056
+ contextIncluded?: Record<string, unknown>;
1057
+ sessionId?: string | null;
1058
+ contentHash?: string;
1059
+ templateVersion?: number;
1060
+ confidence?: number;
1061
+ templateId?: string | null;
1062
+ isPinned?: boolean;
1063
+ }): Promise<{ entry: unknown }> {
1064
+ return this.request("POST", "/prompt-history", data);
1065
+ }
1066
+
1067
+ async recordPromptHistoryFeedback(
1068
+ sessionId: string,
1069
+ outcome: "success" | "blocker" | "neutral",
1070
+ ): Promise<{ adjusted: number }> {
1071
+ return this.request("POST", "/prompt-history/feedback", {
1072
+ sessionId,
1073
+ outcome,
1074
+ });
1075
+ }
1076
+
1077
+ async getPromptHistoryCohort(contentHash: string): Promise<{
1078
+ cohort: Array<{
1079
+ status: string | null;
1080
+ progressPercent: number | null;
1081
+ hadBlockers: boolean;
1082
+ }>;
1083
+ }> {
1084
+ const params = new URLSearchParams({ content_hash: contentHash });
1085
+ return this.request("GET", `/prompt-history/cohort?${params.toString()}`);
1086
+ }
1087
+
1066
1088
  // ============ PROMPT GENERATION ============
1067
1089
 
1068
1090
  /**
@@ -1083,6 +1105,8 @@ export class HarmonyApiClient {
1083
1105
  includeLinks: boolean;
1084
1106
  includeDescription: boolean;
1085
1107
  }>;
1108
+ /** Optional active session ID to associate with the prompt snapshot. */
1109
+ sessionId?: string | null;
1086
1110
  }): Promise<{
1087
1111
  prompt: string;
1088
1112
  variant: string;
@@ -1101,14 +1125,42 @@ export class HarmonyApiClient {
1101
1125
  cardId: string;
1102
1126
  shortId: number;
1103
1127
  title: string;
1128
+ /** Local UUID identifying the persisted snapshot (AGP P2). */
1129
+ promptId: string;
1130
+ /** SHA-256 of the generated prompt body (AGP P2 cohort key). */
1131
+ contentHash: string;
1132
+ /** Template version that produced this prompt. */
1133
+ version: number;
1104
1134
  }> {
1105
- const { assembleContext, cacheManifest, generatePrompt } =
1106
- await loadPromptModules();
1135
+ const { generatePrompt } = await loadPromptModules();
1107
1136
 
1108
1137
  // Fetch card data
1109
1138
  const cardResult = await this.getCard(options.cardId);
1110
1139
  const cardData = cardResult.card as CardPromptData;
1111
1140
 
1141
+ // Fetch card reference links so the prompt can render the "Related Cards"
1142
+ // section and the blocker-aware "Recommended Next Step" synthesis.
1143
+ // Best-effort — link fetch failures must not break prompt generation.
1144
+ try {
1145
+ const linksResult = await this.getCardLinks(options.cardId);
1146
+ const rawLinks =
1147
+ (linksResult.links as Array<{
1148
+ link_type: "relates_to" | "blocks" | "duplicates" | "is_part_of";
1149
+ direction: "outgoing" | "incoming";
1150
+ target_card: { short_id: number; title: string } | null;
1151
+ }>) || [];
1152
+ cardData.links = rawLinks
1153
+ .filter((l) => l.target_card)
1154
+ .map((l) => ({
1155
+ target_card: l.target_card as { short_id: number; title: string },
1156
+ direction: l.direction,
1157
+ display_type: getDisplayLinkType(l.link_type, l.direction),
1158
+ }));
1159
+ } catch (err) {
1160
+ const msg = err instanceof Error ? err.message : String(err);
1161
+ console.debug(`[generateCardPrompt] getCardLinks failed: ${msg}`);
1162
+ }
1163
+
1112
1164
  // Try to get column info
1113
1165
  let columnData: { name: string } | null = null;
1114
1166
  const projectIdForBoard = options.projectId || cardData.project_id;
@@ -1128,67 +1180,37 @@ export class HarmonyApiClient {
1128
1180
 
1129
1181
  const variant = options.variant || "execute";
1130
1182
 
1131
- // Assemble memory context
1132
- let assembledContextStr: string | undefined;
1133
- let assemblyId: string | undefined;
1183
+ // Phase 0 (memory architecture v2): full context assembly removed.
1184
+ // Use the basic memory search path so callers still get _some_ memory
1185
+ // hints. Phase 1 will reintroduce a session-scoped working memory layer.
1186
+ const assembledContextStr: string | undefined = undefined;
1187
+ const assemblyId: string | undefined = undefined;
1134
1188
  let memories: MemoryItem[] | undefined;
1135
1189
 
1136
1190
  try {
1137
1191
  if (options.workspaceId && cardData.title) {
1138
- const cardLabels = (cardData.labels || []).map((l) => l.name);
1139
- const taskContext = [cardData.title, cardData.description || ""]
1140
- .filter(Boolean)
1141
- .join(" ");
1142
-
1143
- const assembled = await assembleContext({
1144
- workspaceId: options.workspaceId,
1145
- projectId: options.projectId,
1146
- taskContext,
1147
- cardLabels,
1148
- cardId: cardData.id,
1149
- client: this,
1150
- });
1151
-
1152
- if (assembled.context) {
1153
- assembledContextStr = assembled.context;
1154
- assemblyId = assembled.manifest.assemblyId;
1155
- cacheManifest(assembled.manifest);
1192
+ const memoryResult = await this.searchMemoryEntities(
1193
+ options.workspaceId,
1194
+ cardData.title,
1195
+ {
1196
+ project_id: options.projectId,
1197
+ limit: 5,
1198
+ },
1199
+ );
1200
+ if (memoryResult.entities?.length > 0) {
1201
+ memories = (memoryResult.entities as MemoryItem[]).map((e) => ({
1202
+ id: e.id,
1203
+ type: e.type,
1204
+ title: e.title,
1205
+ content: e.content,
1206
+ confidence: e.confidence,
1207
+ tags: e.tags || [],
1208
+ }));
1156
1209
  }
1157
1210
  }
1158
1211
  } catch (err) {
1159
- // Context assembly failed, try legacy fallback
1160
1212
  const msg = err instanceof Error ? err.message : String(err);
1161
- console.debug(`[generateCardPrompt] Context assembly failed: ${msg}`);
1162
- try {
1163
- if (options.workspaceId && cardData.title) {
1164
- const memoryResult = await this.searchMemoryEntities(
1165
- options.workspaceId,
1166
- cardData.title,
1167
- {
1168
- project_id: options.projectId,
1169
- limit: 5,
1170
- },
1171
- );
1172
- if (memoryResult.entities?.length > 0) {
1173
- memories = (memoryResult.entities as MemoryItem[]).map((e) => ({
1174
- id: e.id,
1175
- type: e.type,
1176
- title: e.title,
1177
- content: e.content,
1178
- confidence: e.confidence,
1179
- tags: e.tags || [],
1180
- }));
1181
- }
1182
- }
1183
- } catch (fallbackErr) {
1184
- const fallbackMsg =
1185
- fallbackErr instanceof Error
1186
- ? fallbackErr.message
1187
- : String(fallbackErr);
1188
- console.debug(
1189
- `[generateCardPrompt] Memory fallback also failed: ${fallbackMsg}`,
1190
- );
1191
- }
1213
+ console.debug(`[generateCardPrompt] Memory search failed: ${msg}`);
1192
1214
  }
1193
1215
 
1194
1216
  const result = generatePrompt({
@@ -1202,6 +1224,30 @@ export class HarmonyApiClient {
1202
1224
  assemblyId,
1203
1225
  });
1204
1226
 
1227
+ // AGP P2: persist a session-linked snapshot. Best-effort — never fail
1228
+ // prompt generation just because logging didn't land.
1229
+ try {
1230
+ await this.recordPromptHistory({
1231
+ cardId: cardData.id,
1232
+ generatedPrompt: result.prompt,
1233
+ variant: variant as "analysis" | "draft" | "execute",
1234
+ contextIncluded: {
1235
+ assemblyId: result.assemblyId ?? null,
1236
+ tokenEstimate: result.tokenEstimate,
1237
+ contextSummary: result.contextSummary,
1238
+ },
1239
+ sessionId: options.sessionId ?? null,
1240
+ contentHash: result.contentHash,
1241
+ templateVersion: result.version,
1242
+ confidence: 0.5,
1243
+ });
1244
+ } catch (err) {
1245
+ const msg = err instanceof Error ? err.message : String(err);
1246
+ console.debug(
1247
+ `[generateCardPrompt] prompt_history persistence failed: ${msg}`,
1248
+ );
1249
+ }
1250
+
1205
1251
  return {
1206
1252
  ...result,
1207
1253
  cardId: cardData.id,
@@ -1241,14 +1287,10 @@ interface MemoryItem {
1241
1287
  tags: string[];
1242
1288
  }
1243
1289
 
1244
- // Cached dynamic imports for context-assembly and prompt-builder
1290
+ // Cached dynamic import for prompt-builder.
1291
+ // Phase 0 (memory architecture v2): context-assembly module deleted; prompt
1292
+ // generation falls back to a basic memory search path until Phase 1.
1245
1293
  let _promptModules: {
1246
- assembleContext: Awaited<
1247
- typeof import("./context-assembly.js")
1248
- >["assembleContext"];
1249
- cacheManifest: Awaited<
1250
- typeof import("./context-assembly.js")
1251
- >["cacheManifest"];
1252
1294
  generatePrompt: Awaited<
1253
1295
  typeof import("./prompt-builder.js")
1254
1296
  >["generatePrompt"];
@@ -1256,13 +1298,8 @@ let _promptModules: {
1256
1298
 
1257
1299
  async function loadPromptModules() {
1258
1300
  if (!_promptModules) {
1259
- const [ca, pb] = await Promise.all([
1260
- import("./context-assembly.js"),
1261
- import("./prompt-builder.js"),
1262
- ]);
1301
+ const pb = await import("./prompt-builder.js");
1263
1302
  _promptModules = {
1264
- assembleContext: ca.assembleContext,
1265
- cacheManifest: ca.cacheManifest,
1266
1303
  generatePrompt: pb.generatePrompt,
1267
1304
  };
1268
1305
  }