@contextstream/mcp-server 0.4.48 → 0.4.50

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.
package/dist/index.js CHANGED
@@ -591,7 +591,7 @@ function buildHooksConfig(options) {
591
591
  ],
592
592
  UserPromptSubmit: userPromptHooks
593
593
  };
594
- if (options?.includePreCompact) {
594
+ if (options?.includePreCompact !== false) {
595
595
  config.PreCompact = [
596
596
  {
597
597
  // Match both manual (/compact) and automatic compaction
@@ -643,7 +643,7 @@ async function installHookScripts(options) {
643
643
  preToolUse: "npx @contextstream/mcp-server hook pre-tool-use",
644
644
  userPrompt: "npx @contextstream/mcp-server hook user-prompt-submit"
645
645
  };
646
- if (options?.includePreCompact) {
646
+ if (options?.includePreCompact !== false) {
647
647
  result.preCompact = "npx @contextstream/mcp-server hook pre-compact";
648
648
  }
649
649
  if (options?.includeMediaAware !== false) {
@@ -689,7 +689,7 @@ async function installClaudeCodeHooks(options) {
689
689
  "npx @contextstream/mcp-server hook pre-tool-use",
690
690
  "npx @contextstream/mcp-server hook user-prompt-submit"
691
691
  );
692
- if (options.includePreCompact) {
692
+ if (options.includePreCompact !== false) {
693
693
  result.scripts.push("npx @contextstream/mcp-server hook pre-compact");
694
694
  }
695
695
  if (options.includeMediaAware !== false) {
@@ -756,12 +756,11 @@ When Media-Aware hook detects media patterns, it injects context about:
756
756
  - How to get clips for Remotion (with frame-based props)
757
757
  - How to index new media files
758
758
 
759
- ### PreCompact Hook (Optional)
759
+ ### PreCompact Hook
760
760
  - **Command:** \`npx @contextstream/mcp-server hook pre-compact\`
761
761
  - **Purpose:** Saves conversation state before context compaction
762
762
  - **Triggers:** Both manual (/compact) and automatic compaction
763
- - **Disable:** Set \`CONTEXTSTREAM_PRECOMPACT_ENABLED=false\` environment variable
764
- - **Note:** Enable with \`generate_rules(include_pre_compact=true)\` to activate
763
+ - **Installed:** By default (disable with \`CONTEXTSTREAM_HOOK_ENABLED=false\`)
765
764
 
766
765
  When PreCompact runs, it:
767
766
  1. Parses the transcript for active files and tool calls
@@ -2633,11 +2632,16 @@ var init_user_prompt_submit = __esm({
2633
2632
  "src/hooks/user-prompt-submit.ts"() {
2634
2633
  "use strict";
2635
2634
  ENABLED3 = process.env.CONTEXTSTREAM_REMINDER_ENABLED !== "false";
2636
- REMINDER = `[CONTEXTSTREAM RULES]
2637
- 1. BEFORE Glob/Grep/Read/Search: mcp__contextstream__search(mode="hybrid") FIRST
2638
- 2. Call context_smart at start of EVERY response
2639
- 3. Local tools ONLY if ContextStream returns 0 results
2640
- [END RULES]`;
2635
+ REMINDER = `[CONTEXTSTREAM - DO THIS FIRST]
2636
+ mcp__contextstream__context(user_message="<user's message>") - CALL THIS BEFORE ANY OTHER TOOL
2637
+
2638
+ Why: Delivers task-specific rules, lessons from past mistakes, and relevant context.
2639
+ Skip it = repeat past mistakes, miss relevant decisions, fly blind.
2640
+
2641
+ BEFORE Glob/Grep/Read/Search/Explore:
2642
+ mcp__contextstream__search(mode="hybrid", query="...") FIRST
2643
+ Local tools ONLY if search returns 0 results.
2644
+ [END]`;
2641
2645
  isDirectRun3 = process.argv[1]?.includes("user-prompt-submit") || process.argv[2] === "user-prompt-submit";
2642
2646
  if (isDirectRun3) {
2643
2647
  runUserPromptSubmitHook().catch(() => process.exit(0));
@@ -2821,6 +2825,9 @@ function parseTranscript(transcriptPath) {
2821
2825
  const activeFiles = /* @__PURE__ */ new Set();
2822
2826
  const recentMessages = [];
2823
2827
  const toolCalls = [];
2828
+ const messages = [];
2829
+ let startedAt = (/* @__PURE__ */ new Date()).toISOString();
2830
+ let firstTimestamp = true;
2824
2831
  try {
2825
2832
  const content = fs10.readFileSync(transcriptPath, "utf-8");
2826
2833
  const lines = content.split("\n");
@@ -2829,6 +2836,11 @@ function parseTranscript(transcriptPath) {
2829
2836
  try {
2830
2837
  const entry = JSON.parse(line);
2831
2838
  const msgType = entry.type || "";
2839
+ const timestamp = entry.timestamp || (/* @__PURE__ */ new Date()).toISOString();
2840
+ if (firstTimestamp && entry.timestamp) {
2841
+ startedAt = entry.timestamp;
2842
+ firstTimestamp = false;
2843
+ }
2832
2844
  if (msgType === "tool_use") {
2833
2845
  const toolName = entry.name || "";
2834
2846
  const toolInput = entry.input || {};
@@ -2844,11 +2856,40 @@ function parseTranscript(transcriptPath) {
2844
2856
  activeFiles.add(`[glob:${pattern}]`);
2845
2857
  }
2846
2858
  }
2847
- }
2848
- if (msgType === "assistant" && entry.content) {
2849
- const content2 = entry.content;
2850
- if (typeof content2 === "string" && content2.length > 50) {
2851
- recentMessages.push(content2.slice(0, 500));
2859
+ messages.push({
2860
+ role: "assistant",
2861
+ content: `[Tool: ${toolName}]`,
2862
+ timestamp,
2863
+ tool_calls: { name: toolName, input: toolInput }
2864
+ });
2865
+ } else if (msgType === "tool_result") {
2866
+ const resultContent = typeof entry.content === "string" ? entry.content.slice(0, 2e3) : JSON.stringify(entry.content || {}).slice(0, 2e3);
2867
+ messages.push({
2868
+ role: "tool",
2869
+ content: resultContent,
2870
+ timestamp,
2871
+ tool_results: { name: entry.name }
2872
+ });
2873
+ } else if (msgType === "user" || entry.role === "user") {
2874
+ const userContent = typeof entry.content === "string" ? entry.content : "";
2875
+ if (userContent) {
2876
+ messages.push({
2877
+ role: "user",
2878
+ content: userContent,
2879
+ timestamp
2880
+ });
2881
+ }
2882
+ } else if (msgType === "assistant" || entry.role === "assistant") {
2883
+ const assistantContent = typeof entry.content === "string" ? entry.content : "";
2884
+ if (assistantContent) {
2885
+ messages.push({
2886
+ role: "assistant",
2887
+ content: assistantContent,
2888
+ timestamp
2889
+ });
2890
+ if (assistantContent.length > 50) {
2891
+ recentMessages.push(assistantContent.slice(0, 500));
2892
+ }
2852
2893
  }
2853
2894
  }
2854
2895
  } catch {
@@ -2861,10 +2902,56 @@ function parseTranscript(transcriptPath) {
2861
2902
  activeFiles: Array.from(activeFiles).slice(-20),
2862
2903
  // Last 20 files
2863
2904
  toolCallCount: toolCalls.length,
2864
- messageCount: recentMessages.length,
2865
- lastTools: toolCalls.slice(-10).map((t) => t.name)
2905
+ messageCount: messages.length,
2906
+ lastTools: toolCalls.slice(-10).map((t) => t.name),
2866
2907
  // Last 10 tool names
2908
+ messages,
2909
+ startedAt
2910
+ };
2911
+ }
2912
+ async function saveFullTranscript(sessionId, transcriptData, trigger) {
2913
+ if (!API_KEY2) {
2914
+ return { success: false, message: "No API key configured" };
2915
+ }
2916
+ if (transcriptData.messages.length === 0) {
2917
+ return { success: false, message: "No messages to save" };
2918
+ }
2919
+ const payload = {
2920
+ session_id: sessionId,
2921
+ messages: transcriptData.messages,
2922
+ started_at: transcriptData.startedAt,
2923
+ source_type: "pre_compact",
2924
+ title: `Pre-compaction save (${trigger})`,
2925
+ metadata: {
2926
+ trigger,
2927
+ active_files: transcriptData.activeFiles,
2928
+ tool_call_count: transcriptData.toolCallCount
2929
+ },
2930
+ tags: ["pre_compaction", trigger]
2867
2931
  };
2932
+ if (WORKSPACE_ID) {
2933
+ payload.workspace_id = WORKSPACE_ID;
2934
+ }
2935
+ try {
2936
+ const controller = new AbortController();
2937
+ const timeoutId = setTimeout(() => controller.abort(), 1e4);
2938
+ const response = await fetch(`${API_URL2}/api/v1/transcripts`, {
2939
+ method: "POST",
2940
+ headers: {
2941
+ "Content-Type": "application/json",
2942
+ "X-API-Key": API_KEY2
2943
+ },
2944
+ body: JSON.stringify(payload),
2945
+ signal: controller.signal
2946
+ });
2947
+ clearTimeout(timeoutId);
2948
+ if (response.ok) {
2949
+ return { success: true, message: `Transcript saved (${transcriptData.messages.length} messages)` };
2950
+ }
2951
+ return { success: false, message: `API error: ${response.status}` };
2952
+ } catch (error) {
2953
+ return { success: false, message: String(error) };
2954
+ }
2868
2955
  }
2869
2956
  async function saveSnapshot(sessionId, transcriptData, trigger) {
2870
2957
  if (!API_KEY2) {
@@ -2946,13 +3033,19 @@ async function runPreCompactHook() {
2946
3033
  }
2947
3034
  let autoSaveStatus = "";
2948
3035
  if (AUTO_SAVE && API_KEY2) {
2949
- const { success, message } = await saveSnapshot(sessionId, transcriptData, trigger);
2950
- if (success) {
3036
+ const transcriptResult = await saveFullTranscript(sessionId, transcriptData, trigger);
3037
+ if (transcriptResult.success) {
2951
3038
  autoSaveStatus = `
2952
- [ContextStream: Auto-saved snapshot with ${transcriptData.activeFiles.length} active files]`;
3039
+ [ContextStream: ${transcriptResult.message}]`;
2953
3040
  } else {
2954
- autoSaveStatus = `
3041
+ const { success, message } = await saveSnapshot(sessionId, transcriptData, trigger);
3042
+ if (success) {
3043
+ autoSaveStatus = `
3044
+ [ContextStream: Auto-saved snapshot with ${transcriptData.activeFiles.length} active files (transcript save failed: ${transcriptResult.message})]`;
3045
+ } else {
3046
+ autoSaveStatus = `
2955
3047
  [ContextStream: Auto-save failed - ${message}]`;
3048
+ }
2956
3049
  }
2957
3050
  }
2958
3051
  const filesList = transcriptData.activeFiles.slice(0, 5).join(", ") || "none detected";
@@ -9931,6 +10024,35 @@ var ContextStreamClient = class {
9931
10024
  }
9932
10025
  });
9933
10026
  }
10027
+ /**
10028
+ * Capture a memory event with a direct event_type.
10029
+ * Used for auto-save session snapshots and other system events.
10030
+ */
10031
+ async captureMemoryEvent(params) {
10032
+ const withDefaults = this.withDefaults(params);
10033
+ const metadata = {
10034
+ ...params.metadata || {}
10035
+ };
10036
+ if (params.tags && params.tags.length > 0) {
10037
+ metadata.tags = params.tags;
10038
+ }
10039
+ if (!metadata.captured_at) {
10040
+ metadata.captured_at = (/* @__PURE__ */ new Date()).toISOString();
10041
+ }
10042
+ if (!metadata.source) {
10043
+ metadata.source = "mcp_auto_capture";
10044
+ }
10045
+ return this.createMemoryEvent({
10046
+ workspace_id: withDefaults.workspace_id,
10047
+ project_id: withDefaults.project_id,
10048
+ event_type: params.event_type,
10049
+ title: params.title,
10050
+ content: params.content,
10051
+ provenance: params.provenance,
10052
+ code_refs: params.code_refs,
10053
+ metadata
10054
+ });
10055
+ }
9934
10056
  submitContextFeedback(body) {
9935
10057
  return request(this.config, "/context/smart/feedback", { body: this.withDefaults(body) });
9936
10058
  }
@@ -10498,7 +10620,11 @@ var ContextStreamClient = class {
10498
10620
  notice_inline: false,
10499
10621
  // Session token tracking for context pressure
10500
10622
  ...params.session_tokens !== void 0 && { session_tokens: params.session_tokens },
10501
- ...params.context_threshold !== void 0 && { context_threshold: params.context_threshold }
10623
+ ...params.context_threshold !== void 0 && { context_threshold: params.context_threshold },
10624
+ // Transcript save parameters
10625
+ ...params.save_exchange !== void 0 && { save_exchange: params.save_exchange },
10626
+ ...params.session_id !== void 0 && { session_id: params.session_id },
10627
+ ...params.client_name !== void 0 && { client_name: params.client_name }
10502
10628
  }
10503
10629
  });
10504
10630
  const data = unwrapApiResponse(apiResult);
@@ -12060,6 +12186,95 @@ ${context}`;
12060
12186
  uuidSchema.parse(params.doc_id);
12061
12187
  return request(this.config, `/docs/${params.doc_id}`, { method: "DELETE" });
12062
12188
  }
12189
+ // -------------------------------------------------------------------------
12190
+ // Transcript methods (conversation session storage)
12191
+ // -------------------------------------------------------------------------
12192
+ /**
12193
+ * List transcripts for a workspace/project
12194
+ */
12195
+ async listTranscripts(params) {
12196
+ const withDefaults = this.withDefaults(params || {});
12197
+ const query = new URLSearchParams();
12198
+ if (withDefaults.workspace_id) query.set("workspace_id", withDefaults.workspace_id);
12199
+ if (withDefaults.project_id) query.set("project_id", withDefaults.project_id);
12200
+ if (params?.session_id) query.set("session_id", params.session_id);
12201
+ if (params?.client_name) query.set("client_name", params.client_name);
12202
+ if (params?.started_after) query.set("started_after", params.started_after);
12203
+ if (params?.started_before) query.set("started_before", params.started_before);
12204
+ if (params?.limit) query.set("per_page", String(params.limit));
12205
+ if (params?.page) query.set("page", String(params.page));
12206
+ if (params?.per_page) query.set("per_page", String(params.per_page));
12207
+ const suffix = query.toString() ? `?${query.toString()}` : "";
12208
+ return request(this.config, `/transcripts${suffix}`, { method: "GET" });
12209
+ }
12210
+ /**
12211
+ * Get a specific transcript by ID
12212
+ */
12213
+ async getTranscript(transcript_id) {
12214
+ uuidSchema.parse(transcript_id);
12215
+ return request(this.config, `/transcripts/${transcript_id}`, { method: "GET" });
12216
+ }
12217
+ /**
12218
+ * Search transcripts by content
12219
+ */
12220
+ async searchTranscripts(params) {
12221
+ const withDefaults = this.withDefaults(params);
12222
+ const queryParams = new URLSearchParams();
12223
+ if (withDefaults.workspace_id) queryParams.set("workspace_id", withDefaults.workspace_id);
12224
+ queryParams.set("query", params.query);
12225
+ if (params.limit) queryParams.set("limit", String(params.limit));
12226
+ return request(this.config, `/transcripts/search?${queryParams.toString()}`, { method: "GET" });
12227
+ }
12228
+ /**
12229
+ * Delete a transcript
12230
+ */
12231
+ async deleteTranscript(transcript_id) {
12232
+ uuidSchema.parse(transcript_id);
12233
+ return request(this.config, `/transcripts/${transcript_id}`, { method: "DELETE" });
12234
+ }
12235
+ // -------------------------------------------------------------------------
12236
+ // Suggested Rules methods (ML-generated rule suggestions)
12237
+ // -------------------------------------------------------------------------
12238
+ /**
12239
+ * List suggested rules
12240
+ */
12241
+ async listSuggestedRules(params) {
12242
+ const withDefaults = this.withDefaults(params || {});
12243
+ const queryParams = new URLSearchParams();
12244
+ if (withDefaults.workspace_id) queryParams.set("workspace_id", withDefaults.workspace_id);
12245
+ if (params?.status) queryParams.set("status", params.status);
12246
+ if (params?.source_type) queryParams.set("source_type", params.source_type);
12247
+ if (params?.min_confidence) queryParams.set("min_confidence", String(params.min_confidence));
12248
+ if (params?.limit) queryParams.set("limit", String(params.limit));
12249
+ if (params?.offset) queryParams.set("offset", String(params.offset));
12250
+ return request(this.config, `/suggested-rules?${queryParams.toString()}`, { method: "GET" });
12251
+ }
12252
+ /**
12253
+ * Get pending suggested rules count
12254
+ */
12255
+ async getSuggestedRulesPendingCount(params) {
12256
+ const withDefaults = this.withDefaults(params || {});
12257
+ const queryParams = new URLSearchParams();
12258
+ if (withDefaults.workspace_id) queryParams.set("workspace_id", withDefaults.workspace_id);
12259
+ return request(this.config, `/suggested-rules/pending-count?${queryParams.toString()}`, { method: "GET" });
12260
+ }
12261
+ /**
12262
+ * Get suggested rules feedback stats
12263
+ */
12264
+ async getSuggestedRulesStats(params) {
12265
+ const withDefaults = this.withDefaults(params || {});
12266
+ const queryParams = new URLSearchParams();
12267
+ if (withDefaults.workspace_id) queryParams.set("workspace_id", withDefaults.workspace_id);
12268
+ return request(this.config, `/suggested-rules/stats?${queryParams.toString()}`, { method: "GET" });
12269
+ }
12270
+ /**
12271
+ * Perform action on suggested rule (accept/reject/modify)
12272
+ */
12273
+ async suggestedRuleAction(params) {
12274
+ uuidSchema.parse(params.rule_id);
12275
+ const { rule_id, ...body } = params;
12276
+ return request(this.config, `/suggested-rules/${rule_id}/action`, { method: "POST", body });
12277
+ }
12063
12278
  };
12064
12279
 
12065
12280
  // src/tools.ts
@@ -12103,62 +12318,103 @@ function applyMcpToolPrefix(markdown, toolPrefix) {
12103
12318
  const toolRegex = new RegExp(`(?<!__)\\b(${toolPattern})\\b(?=\\s*\\()`, "g");
12104
12319
  return markdown.replace(toolRegex, `${toolPrefix}$1`);
12105
12320
  }
12106
- var CONTEXTSTREAM_RULES_DYNAMIC = `
12107
- # ContextStream Rules (MANDATORY)
12321
+ var CONTEXTSTREAM_RULES_BOOTSTRAP = `
12322
+ <contextstream_rules>
12323
+ | Message | Required |
12324
+ |---------|----------|
12325
+ | **1st message** | \`init()\` \u2192 \`context(user_message="...")\` |
12326
+ | **Every message** | \`context(user_message="...")\` FIRST |
12327
+ | **Before file search** | \`search(mode="hybrid")\` BEFORE Glob/Grep/Read |
12328
+ </contextstream_rules>
12108
12329
 
12109
- ## \u{1F6A8} CRITICAL: Call These Tools or Fail \u{1F6A8}
12330
+ **Why?** \`context()\` delivers task-specific rules, lessons from past mistakes, and relevant decisions. Skip it = fly blind.
12110
12331
 
12111
- <contextstream_protocol>
12112
- **EVERY response MUST start with:**
12113
- 1. \`init()\` (1st message only) \u2192 then \`context(user_message="<msg>")\`
12114
- 2. \`context(user_message="<msg>")\` (EVERY subsequent message)
12332
+ **Hooks:** \`<system-reminder>\` tags contain injected instructions \u2014 follow them exactly.
12115
12333
 
12116
- **BEFORE Glob/Grep/Read/Search/Explore:**
12117
- \u2192 \`search(mode="hybrid", query="...")\` FIRST \u2014 local tools ONLY if 0 results
12334
+ **Notices:** [LESSONS_WARNING] \u2192 tell user first | [RULES_NOTICE] \u2192 run \`generate_rules()\` | [VERSION_NOTICE] \u2192 tell user to update
12118
12335
 
12119
- **HOOKS: Follow \`<system-reminder>\` instructions \u2014 they ARE the rules**
12120
- </contextstream_protocol>
12336
+ v${RULES_VERSION}
12337
+ `.trim();
12338
+ var CONTEXTSTREAM_RULES_DYNAMIC = CONTEXTSTREAM_RULES_BOOTSTRAP;
12339
+ var CONTEXTSTREAM_RULES_FULL = `
12340
+ ## \u{1F6A8} MANDATORY: CALL CONTEXT EVERY MESSAGE \u{1F6A8}
12121
12341
 
12122
- ### Why This Matters
12342
+ <contextstream_rules>
12343
+ **EVERY response MUST start with \`context(user_message="...")\`**
12123
12344
 
12124
- - **\`context()\`** = Your rules, lessons, and relevant memories delivered dynamically
12125
- - **\`search()\`** = Indexed search (1 call) vs Grep\u2192Read\u2192Read (4+ calls)
12126
- - **Hooks inject \`<system-reminder>\` tags** = Follow these instructions, they enforce ContextStream-first
12127
- - **Skipping these = missing context, repeated mistakes, slow searches**
12345
+ | Message | What to Call |
12346
+ |---------|--------------|
12347
+ | 1st message | \`init()\` \u2192 \`context(user_message="<msg>")\` |
12348
+ | **EVERY message after** | \`context(user_message="<msg>")\` **FIRST** |
12128
12349
 
12129
- ### Protocol
12350
+ **BEFORE Glob/Grep/Read/Search:** \u2192 \`search(mode="hybrid")\` FIRST
12351
+ </contextstream_rules>
12130
12352
 
12131
- | When | Do This |
12132
- |------|---------|
12133
- | 1st message | \`init()\` \u2192 \`context(user_message="...")\` |
12134
- | Every message after | \`context(user_message="...")\` |
12135
- | Before ANY file search | \`search(mode="hybrid")\` first |
12136
- | On \`<system-reminder>\` | **Follow the instructions inside** |
12137
- | Save decisions | \`session(action="capture", ...)\` |
12353
+ **This block MUST appear at the start of EVERY response.** Failing to call \`context()\` means missing rules, lessons, and relevant context.
12354
+
12355
+ ---
12356
+
12357
+ ## Why \`context()\` is MANDATORY Every Message
12358
+
12359
+ \u274C **WRONG:** "I already called init, I don't need context"
12360
+ \u2705 **CORRECT:** \`context()\` is required EVERY message, not just the first
12361
+
12362
+ **What you lose without \`context()\`:**
12363
+ - Dynamic rules matched to your current task
12364
+ - Lessons from past mistakes (you WILL repeat them)
12365
+ - Semantically relevant decisions and context
12366
+ - Warnings about risky operations
12367
+
12368
+ **\`init()\` gives recent items by TIME. \`context()\` finds items RELEVANT to THIS message.**
12369
+
12370
+ ---
12138
12371
 
12139
- ### Handle Notices from context()
12372
+ ## Handle Notices from context()
12140
12373
 
12141
12374
  - **[LESSONS_WARNING]** \u2192 Tell user about past mistakes BEFORE proceeding
12142
12375
  - **[RULES_NOTICE]** \u2192 Run \`generate_rules()\` to update
12143
12376
  - **[VERSION_NOTICE]** \u2192 Tell user to update MCP
12144
12377
 
12145
- ### Fast Path (Skip context only for these)
12378
+ ---
12146
12379
 
12147
- Simple utility commands: "list workspaces", "show version", "list reminders"
12148
- \u2192 Just execute directly: \`workspace(action="list")\`, \`help(action="version")\`
12380
+ ## \u{1F6A8} HOOKS - AUTOMATIC RULE ENFORCEMENT \u{1F6A8}
12149
12381
 
12150
- Rules Version: ${RULES_VERSION}
12151
- `.trim();
12152
- var CONTEXTSTREAM_RULES_FULL = `
12153
- ## ContextStream Rules
12382
+ **ContextStream installs hooks that automatically enforce rules.** You MUST follow hook output.
12154
12383
 
12155
- <contextstream_rules>
12156
- 1. **SIMPLE UTILITY?** (list/show/version) \u2192 Just execute, skip context calls
12157
- 2. **CODING TASK?** \u2192 init \u2192 context \u2192 work \u2192 capture
12158
- 3. **SEARCH FIRST:** Before Glob/Grep/Read \u2192 \`search(mode="hybrid")\` FIRST
12159
- </contextstream_rules>
12384
+ ### Installed Hooks
12160
12385
 
12161
- **Display this block at the start of responses to keep rules in context.**
12386
+ | Hook | What It Does | Output |
12387
+ |------|--------------|--------|
12388
+ | **UserPromptSubmit** | Injects rules reminder on EVERY message | \`<system-reminder>\` with rules block |
12389
+ | **PreToolUse** | Blocks Glob/Grep/Search/Explore when ContextStream is available | Error message redirecting to \`search()\` |
12390
+ | **PostToolUse** | Auto-indexes files after Edit/Write operations | Background indexing |
12391
+ | **PreCompact** | Saves session state before context compaction | Snapshot creation |
12392
+
12393
+ ### How Hooks Work
12394
+
12395
+ 1. **\`<system-reminder>\` tags** - Injected by UserPromptSubmit hook on every message
12396
+ - These tags contain the current rules
12397
+ - **FOLLOW THE INSTRUCTIONS INSIDE** - they ARE the rules
12398
+ - Example: \`[CONTEXTSTREAM RULES] 1. BEFORE Glob/Grep... [END RULES]\`
12399
+
12400
+ 2. **PreToolUse blocking** - If you try to use Glob/Grep/Search/Explore:
12401
+ - Hook returns error: \`STOP: Use mcp__contextstream__search(mode="hybrid") instead\`
12402
+ - **You MUST use the suggested ContextStream tool instead**
12403
+ - Local tools are only allowed if project is not indexed or ContextStream returns 0 results
12404
+
12405
+ 3. **PostToolUse indexing** - After Edit/Write operations:
12406
+ - Changed files are automatically re-indexed
12407
+ - No action required from you
12408
+
12409
+ 4. **PreCompact snapshots** - Before context compaction:
12410
+ - Hook reminds you to save important state
12411
+ - Call \`session(action="capture", event_type="session_snapshot", ...)\` when warned
12412
+
12413
+ ### Disabling Hooks
12414
+
12415
+ Set environment variable: \`CONTEXTSTREAM_HOOK_ENABLED=false\`
12416
+
12417
+ **Note:** Disabling hooks removes rule enforcement. Only disable for debugging.
12162
12418
 
12163
12419
  ---
12164
12420
 
@@ -12244,27 +12500,27 @@ You have access to ContextStream MCP tools for persistent memory and context.
12244
12500
  v0.4.x uses **~11 consolidated domain tools** for ~75% token reduction vs previous versions.
12245
12501
  Rules Version: ${RULES_VERSION}
12246
12502
 
12247
- ## TL;DR - WHEN TO USE CONTEXT
12503
+ ## TL;DR - CONTEXT EVERY MESSAGE
12248
12504
 
12249
- | Request Type | What to Do |
12250
- |--------------|------------|
12251
- | **\u{1F680} Simple utility** (list workspaces, show version) | **Just execute directly** - skip init, context, capture |
12252
- | **\u{1F4BB} Coding task** (edit, create, refactor) | Full context: init \u2192 context \u2192 work \u2192 capture |
12253
- | **\u{1F50D} Code search/discovery** | init \u2192 context \u2192 search() |
12254
- | **\u26A0\uFE0F Risky work** (deploy, migrate, refactor) | Check lessons first: \`session(action="get_lessons")\` |
12255
- | **User frustration/correction** | Capture lesson: \`session(action="capture_lesson", ...)\` |
12505
+ | Message | Required |
12506
+ |---------|----------|
12507
+ | **1st message** | \`init()\` \u2192 \`context(user_message="<msg>")\` |
12508
+ | **EVERY message after** | \`context(user_message="<msg>")\` **FIRST** |
12509
+ | **Before file search** | \`search(mode="hybrid")\` FIRST |
12510
+ | **After significant work** | \`session(action="capture", event_type="decision", ...)\` |
12511
+ | **User correction** | \`session(action="capture_lesson", ...)\` |
12256
12512
 
12257
- ### Simple Utility Operations - FAST PATH
12513
+ ### Why EVERY Message?
12258
12514
 
12259
- **For simple queries, just execute and respond:**
12260
- - "list workspaces" \u2192 \`workspace(action="list")\` \u2192 done
12261
- - "list projects" \u2192 \`project(action="list")\` \u2192 done
12262
- - "show version" \u2192 \`help(action="version")\` \u2192 done
12263
- - "what reminders do I have" \u2192 \`reminder(action="list")\` \u2192 done
12515
+ \`context()\` delivers:
12516
+ - **Dynamic rules** matched to your current task
12517
+ - **Lessons** from past mistakes (prevents repeating errors)
12518
+ - **Relevant decisions** and context (semantic search)
12519
+ - **Warnings** about risky operations
12264
12520
 
12265
- **No init. No context. No capture.** These add noise, not value.
12521
+ **Without \`context()\`, you are blind to relevant context and will repeat past mistakes.**
12266
12522
 
12267
- ### Coding Tasks - FULL CONTEXT
12523
+ ### Protocol
12268
12524
 
12269
12525
  | Step | What to Call |
12270
12526
  |------|--------------|
@@ -12275,12 +12531,7 @@ Rules Version: ${RULES_VERSION}
12275
12531
  | **User correction** | \`session(action="capture_lesson", ...)\` |
12276
12532
  | **\u26A0\uFE0F When warnings received** | **STOP**, acknowledge, explain mitigation, then proceed |
12277
12533
 
12278
- **How to detect simple utility operations:**
12279
- - Single-word commands: "list", "show", "version", "help"
12280
- - Data retrieval with no context dependency: "list my workspaces", "what projects do I have"
12281
- - Status checks: "am I authenticated?", "what's the server version?"
12282
-
12283
- **First message rule (for coding tasks):** After \`init\`:
12534
+ **First message rule:** After \`init\`:
12284
12535
  1. Check for \`lessons\` in response - if present, READ and SUMMARIZE them to user
12285
12536
  2. Then call \`context\` before any other tool or response
12286
12537
 
@@ -12380,8 +12631,8 @@ ContextStream tracks context pressure to help you stay ahead of conversation com
12380
12631
  - \`prepare_save\`: Start thinking about saving important state
12381
12632
  - \`save_now\`: Immediately call \`session(action="capture", event_type="session_snapshot")\` to preserve state
12382
12633
 
12383
- **PreCompact Hook (Optional):** If enabled, Claude Code will inject a reminder to save state before compaction.
12384
- Enable with: \`generate_rules(install_hooks=true, include_pre_compact=true)\`
12634
+ **PreCompact Hook:** Automatically saves session state before context compaction.
12635
+ Installed by default. Disable with: \`CONTEXTSTREAM_HOOK_ENABLED=false\`
12385
12636
 
12386
12637
  **Before compaction happens (when warned):**
12387
12638
  \`\`\`
@@ -12678,7 +12929,7 @@ Everything else = full protocol (init \u2192 context \u2192 search \u2192 work)
12678
12929
  ### Context Pressure & Compaction
12679
12930
 
12680
12931
  - If \`context\` returns high/critical \`context_pressure\`: call \`session(action="capture", ...)\` to save state
12681
- - PreCompact hooks automatically save snapshots before compaction (if installed)
12932
+ - PreCompact hooks automatically save snapshots before compaction
12682
12933
 
12683
12934
  ### Enhanced Context (Warnings)
12684
12935
 
@@ -12793,8 +13044,8 @@ function getTemplate(editor) {
12793
13044
  function generateRuleContent(editor, options) {
12794
13045
  const template = getTemplate(editor);
12795
13046
  if (!template) return null;
12796
- const mode = options?.mode || "dynamic";
12797
- const rules = mode === "full" ? CONTEXTSTREAM_RULES_FULL : mode === "minimal" ? CONTEXTSTREAM_RULES_MINIMAL : CONTEXTSTREAM_RULES_DYNAMIC;
13047
+ const mode = options?.mode || "bootstrap";
13048
+ const rules = mode === "full" ? CONTEXTSTREAM_RULES_FULL : mode === "minimal" ? CONTEXTSTREAM_RULES_MINIMAL : mode === "bootstrap" ? CONTEXTSTREAM_RULES_BOOTSTRAP : CONTEXTSTREAM_RULES_DYNAMIC;
12798
13049
  let content = template.build(rules);
12799
13050
  if (options?.workspaceName || options?.projectName) {
12800
13051
  const header = `
@@ -13070,6 +13321,85 @@ function trackToolTokenSavings(client, tool, contextText, params, extraMetadata)
13070
13321
  }
13071
13322
  }
13072
13323
 
13324
+ // src/microcopy.ts
13325
+ function getSessionInitTip(sessionId) {
13326
+ return `Session ${sessionId.slice(0, 8)}... initialized. Use context(user_message="...") to get relevant context.`;
13327
+ }
13328
+ function getCaptureHint(eventType) {
13329
+ switch (eventType) {
13330
+ case "decision":
13331
+ return "Decision captured. It will surface in future context() calls when relevant.";
13332
+ case "preference":
13333
+ return "Preference saved. Future sessions will respect this preference.";
13334
+ case "insight":
13335
+ return "Insight captured. Use recall() to retrieve it later.";
13336
+ case "task":
13337
+ return "Task captured. Use list_tasks() to view all tasks.";
13338
+ case "lesson":
13339
+ return "Lesson saved. It will warn you before similar mistakes.";
13340
+ case "session_snapshot":
13341
+ return "Session state saved. Use restore_context() after compaction.";
13342
+ default:
13343
+ return "Event captured. Use recall() to retrieve related events.";
13344
+ }
13345
+ }
13346
+ function getEmptyStateHint(operation) {
13347
+ switch (operation) {
13348
+ case "get_lessons":
13349
+ return "No lessons found. Lessons are captured when mistakes occur.";
13350
+ case "recall":
13351
+ return "No memories found. Use capture() or remember() to save context.";
13352
+ case "list_plans":
13353
+ return "No plans found. Use capture_plan() to create an implementation plan.";
13354
+ case "list_events":
13355
+ return "No events found. Events are captured as you work.";
13356
+ case "list_tasks":
13357
+ return "No tasks found. Use create_task() to add tasks.";
13358
+ case "list_todos":
13359
+ return "No todos found. Use create_todo() to add quick todos.";
13360
+ case "list_diagrams":
13361
+ return "No diagrams found. Use create_diagram() to save a Mermaid diagram.";
13362
+ case "list_docs":
13363
+ return "No docs found. Use create_doc() to save documentation.";
13364
+ default:
13365
+ return "No results found.";
13366
+ }
13367
+ }
13368
+ function getPlanStatusHint(status) {
13369
+ switch (status) {
13370
+ case "draft":
13371
+ return "Plan saved as draft. Update status to 'active' when ready.";
13372
+ case "active":
13373
+ return "Plan is now active. Create tasks to track implementation.";
13374
+ case "completed":
13375
+ return "Plan completed. Great work!";
13376
+ case "archived":
13377
+ return "Plan archived. It will still appear in searches.";
13378
+ case "abandoned":
13379
+ return "Plan abandoned. Consider capturing lessons learned.";
13380
+ default:
13381
+ return "Plan updated. Changes are preserved.";
13382
+ }
13383
+ }
13384
+ var POST_COMPACT_HINTS = {
13385
+ restored: "Context restored from pre-compaction snapshot.",
13386
+ restored_with_session: (sessionId) => `Context restored from session ${sessionId.slice(0, 8)}... snapshot.`,
13387
+ no_snapshot: "No snapshot found. Session state may be incomplete.",
13388
+ failed: "Failed to restore context. Try recall() to find relevant memories."
13389
+ };
13390
+ var INTEGRATION_HINTS = {
13391
+ connected: "Integration connected and syncing.",
13392
+ not_connected: "Integration not connected. Connect at:",
13393
+ sync_in_progress: "Sync in progress. Results may be incomplete.",
13394
+ sync_complete: "Sync complete. All data is current."
13395
+ };
13396
+ var TASK_HINTS = {
13397
+ created: "Task created. Use update_task() to change status.",
13398
+ completed: "Task completed. Well done!",
13399
+ blocked: "Task blocked. Add blocked_reason for context.",
13400
+ linked_to_plan: "Task linked to plan. Progress will be tracked."
13401
+ };
13402
+
13073
13403
  // src/tools.ts
13074
13404
  var LOG_LEVEL = (process.env.CONTEXTSTREAM_LOG_LEVEL || "normal").toLowerCase();
13075
13405
  var LOG_QUIET = LOG_LEVEL === "quiet";
@@ -13135,6 +13465,7 @@ var lastAutoSavePressureLevel = "";
13135
13465
  var contextCallsSinceLastSave = 0;
13136
13466
  var SEARCH_RULES_REMINDER_ENABLED = process.env.CONTEXTSTREAM_SEARCH_REMINDER?.toLowerCase() !== "false";
13137
13467
  var SEARCH_RULES_REMINDER = `[SEARCH] Use search(mode="hybrid") before Glob/Grep/Read. Local tools only if 0 results.`;
13468
+ var CONTEXT_CALL_REMINDER = `[CONTEXT] Call context(user_message="...") at start of EVERY response. This is MANDATORY.`;
13138
13469
  var LESSONS_REMINDER_PREFIX = `
13139
13470
  \u{1F6A8} [LESSONS_WARNING] Past Mistakes Found - READ BEFORE PROCEEDING!
13140
13471
  \u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501
@@ -14733,7 +15064,10 @@ function registerTools(server, client, sessionManager) {
14733
15064
  "notion_stats",
14734
15065
  "notion_activity",
14735
15066
  "notion_knowledge",
14736
- "notion_summary"
15067
+ "notion_summary",
15068
+ // Media operations (credit-metered)
15069
+ "media_index",
15070
+ "media_search"
14737
15071
  ]);
14738
15072
  const proTools = (() => {
14739
15073
  const raw = process.env.CONTEXTSTREAM_PRO_TOOLS;
@@ -16821,6 +17155,8 @@ This does semantic search on the first message. You only need context on subsequ
16821
17155
  ideRoots
16822
17156
  );
16823
17157
  result.tools_hint = getCoreToolsHint();
17158
+ const sessionId = typeof result.session_id === "string" ? result.session_id : void 0;
17159
+ result.educational_tip = getSessionInitTip(sessionId);
16824
17160
  const shouldRestoreContext = input.is_post_compact ?? RESTORE_CONTEXT_DEFAULT;
16825
17161
  if (shouldRestoreContext) {
16826
17162
  result.is_post_compact = true;
@@ -16890,15 +17226,15 @@ This does semantic search on the first message. You only need context on subsequ
16890
17226
  ...snapshotData
16891
17227
  };
16892
17228
  result.is_post_compact = true;
16893
- result.post_compact_hint = prevSessionId ? `Session restored from session ${prevSessionId}. Review 'restored_context' to continue where you left off.` : "Session restored from pre-compaction snapshot. Review the 'restored_context' to continue where you left off.";
17229
+ result.post_compact_hint = prevSessionId ? POST_COMPACT_HINTS.restored_with_session(prevSessionId) : POST_COMPACT_HINTS.restored;
16894
17230
  } else {
16895
17231
  result.is_post_compact = true;
16896
- result.post_compact_hint = "Post-compaction session started, but no snapshots found. Use context_smart to retrieve relevant context.";
17232
+ result.post_compact_hint = POST_COMPACT_HINTS.no_snapshot;
16897
17233
  }
16898
17234
  } catch (err) {
16899
17235
  logDebug(`Failed to restore post-compact context: ${err}`);
16900
17236
  result.is_post_compact = true;
16901
- result.post_compact_hint = "Post-compaction session started. Snapshot restoration failed, use context_smart for context.";
17237
+ result.post_compact_hint = POST_COMPACT_HINTS.failed;
16902
17238
  }
16903
17239
  }
16904
17240
  }
@@ -16934,7 +17270,7 @@ This does semantic search on the first message. You only need context on subsequ
16934
17270
  slack_connected: intStatus.slack,
16935
17271
  github_connected: intStatus.github,
16936
17272
  auto_hide_enabled: true,
16937
- hint: intStatus.slack || intStatus.github ? "Integration tools are now available in the tool list." : "Connect integrations at https://contextstream.io/settings/integrations to enable Slack/GitHub tools."
17273
+ hint: intStatus.slack || intStatus.github ? "Integration tools are now available in the tool list." : INTEGRATION_HINTS.not_connected + " https://contextstream.io/settings/integrations"
16938
17274
  };
16939
17275
  } catch (error) {
16940
17276
  logDebug(`Failed to check integration status: ${error}`);
@@ -17946,11 +18282,11 @@ Supported editors: ${getAvailableEditors().join(", ")}`,
17946
18282
  workspace_id: external_exports.string().uuid().optional().describe("Workspace ID to include in rules"),
17947
18283
  project_name: external_exports.string().optional().describe("Project name to include in rules"),
17948
18284
  additional_rules: external_exports.string().optional().describe("Additional project-specific rules to append"),
17949
- mode: external_exports.enum(["minimal", "full"]).optional().describe("Rule verbosity mode (default: minimal)"),
17950
- overwrite_existing: external_exports.boolean().optional().describe("Allow overwriting existing rule files (ContextStream block only)"),
18285
+ mode: external_exports.enum(["minimal", "full", "bootstrap"]).optional().default("bootstrap").describe("Rule verbosity: bootstrap (~15 lines, recommended), minimal (~80 lines), full (~600 lines)"),
18286
+ overwrite_existing: external_exports.boolean().optional().default(true).describe("Overwrite ContextStream block in existing rule files (default: true). User content outside the block is preserved."),
17951
18287
  apply_global: external_exports.boolean().optional().describe("Also write global rule files for supported editors"),
17952
18288
  install_hooks: external_exports.boolean().optional().describe("Install Claude Code hooks to enforce ContextStream-first search. Defaults to true for Claude users. Set to false to skip."),
17953
- include_pre_compact: external_exports.boolean().optional().describe("Include PreCompact hook for automatic state saving before context compaction. Defaults to false."),
18289
+ include_pre_compact: external_exports.boolean().optional().describe("Include PreCompact hook for automatic state saving before context compaction. Defaults to true."),
17954
18290
  dry_run: external_exports.boolean().optional().describe("If true, return content without writing files")
17955
18291
  })
17956
18292
  },
@@ -18041,7 +18377,7 @@ Supported editors: ${getAvailableEditors().join(", ")}`,
18041
18377
  { editor, file: "~/.claude/hooks/contextstream-reminder.py", status: "dry run - would create" },
18042
18378
  { editor, file: "~/.claude/settings.json", status: "dry run - would update" }
18043
18379
  );
18044
- if (input.include_pre_compact) {
18380
+ if (input.include_pre_compact !== false) {
18045
18381
  hooksResults.push({ editor, file: "~/.claude/hooks/contextstream-precompact.py", status: "dry run - would create" });
18046
18382
  }
18047
18383
  } else if (editor === "cline") {
@@ -18072,7 +18408,7 @@ Supported editors: ${getAvailableEditors().join(", ")}`,
18072
18408
  const allHookResults = await installAllEditorHooks({
18073
18409
  scope: "global",
18074
18410
  editors: hookSupportedEditors,
18075
- includePreCompact: input.include_pre_compact
18411
+ includePreCompact: input.include_pre_compact !== false
18076
18412
  });
18077
18413
  for (const result of allHookResults) {
18078
18414
  for (const file of result.installed) {
@@ -18428,7 +18764,10 @@ This saves ~80% tokens compared to including full chat history.`,
18428
18764
  mode: external_exports.enum(["standard", "pack"]).optional().describe("Context pack mode (default: pack when enabled)"),
18429
18765
  distill: external_exports.boolean().optional().describe("Use distillation for context pack (default: true)"),
18430
18766
  session_tokens: external_exports.number().optional().describe("Cumulative session token count for context pressure calculation"),
18431
- context_threshold: external_exports.number().optional().describe("Custom context window threshold (defaults to 70k)")
18767
+ context_threshold: external_exports.number().optional().describe("Custom context window threshold (defaults to 70k)"),
18768
+ save_exchange: external_exports.boolean().optional().describe("Save this exchange to the transcript for later search (background task)"),
18769
+ session_id: external_exports.string().optional().describe("Session ID for transcript association (required if save_exchange is true)"),
18770
+ client_name: external_exports.string().optional().describe("Client name for transcript metadata (e.g., 'claude', 'cursor')")
18432
18771
  })
18433
18772
  },
18434
18773
  async (input) => {
@@ -18504,6 +18843,14 @@ This saves ~80% tokens compared to including full chat history.`,
18504
18843
  logDebug(`Failed to restore post-compact context: ${err}`);
18505
18844
  }
18506
18845
  }
18846
+ let sessionId = input.session_id;
18847
+ if (!sessionId && sessionManager && input.save_exchange) {
18848
+ sessionId = sessionManager.getSessionId();
18849
+ }
18850
+ let clientName = input.client_name;
18851
+ if (!clientName && detectedClientInfo) {
18852
+ clientName = detectedClientInfo.name;
18853
+ }
18507
18854
  const result = await client.getSmartContext({
18508
18855
  user_message: input.user_message,
18509
18856
  workspace_id: workspaceId,
@@ -18513,7 +18860,10 @@ This saves ~80% tokens compared to including full chat history.`,
18513
18860
  mode: input.mode,
18514
18861
  distill: input.distill,
18515
18862
  session_tokens: sessionTokens,
18516
- context_threshold: contextThreshold
18863
+ context_threshold: contextThreshold,
18864
+ save_exchange: input.save_exchange,
18865
+ session_id: sessionId,
18866
+ client_name: clientName
18517
18867
  });
18518
18868
  if (sessionManager && result.token_estimate) {
18519
18869
  sessionManager.addTokens(result.token_estimate);
@@ -18659,6 +19009,9 @@ Action: ${cp.suggested_action === "prepare_save" ? "Consider saving important de
18659
19009
  const instructionsLine = result.instructions ? `
18660
19010
 
18661
19011
  [INSTRUCTIONS] ${result.instructions}` : "";
19012
+ const contextRulesLine = `
19013
+
19014
+ ${CONTEXT_CALL_REMINDER}`;
18662
19015
  const allWarnings = [
18663
19016
  serverWarningsLine || lessonsWarningLine,
18664
19017
  // Server warnings OR client-side lesson detection
@@ -18671,6 +19024,8 @@ ${versionWarningLine}` : "",
18671
19024
  contextPressureWarning,
18672
19025
  semanticHints,
18673
19026
  instructionsLine,
19027
+ contextRulesLine,
19028
+ // Reinforce context() must be called every message
18674
19029
  searchRulesLine
18675
19030
  ].filter(Boolean).join("");
18676
19031
  const finalContext = postCompactContext + result.context;
@@ -19805,7 +20160,7 @@ Output formats: full (default, includes content), paths (file paths only - 80% t
19805
20160
  "session",
19806
20161
  {
19807
20162
  title: "Session",
19808
- description: `Session management operations. Actions: capture (save decision/insight), capture_lesson (save lesson from mistake), get_lessons (retrieve lessons), recall (natural language recall), remember (quick save), user_context (get preferences), summary (workspace summary), compress (compress chat), delta (changes since timestamp), smart_search (context-enriched search), decision_trace (trace decision provenance), restore_context (restore state after compaction). Plan actions: capture_plan (save implementation plan), get_plan (retrieve plan with tasks), update_plan (modify plan), list_plans (list all plans). Team actions (team plans only): team_decisions (team-wide decisions), team_lessons (team-wide lessons), team_plans (plans across team workspaces).`,
20163
+ description: `Session management operations. Actions: capture (save decision/insight), capture_lesson (save lesson from mistake), get_lessons (retrieve lessons), recall (natural language recall), remember (quick save), user_context (get preferences), summary (workspace summary), compress (compress chat), delta (changes since timestamp), smart_search (context-enriched search), decision_trace (trace decision provenance), restore_context (restore state after compaction). Plan actions: capture_plan (save implementation plan), get_plan (retrieve plan with tasks), update_plan (modify plan), list_plans (list all plans). Suggested rules actions: list_suggested_rules (view ML-generated rule suggestions), suggested_rule_action (accept/reject/modify a suggestion), suggested_rules_stats (view ML accuracy stats). Team actions (team plans only): team_decisions (team-wide decisions), team_lessons (team-wide lessons), team_plans (plans across team workspaces).`,
19809
20164
  inputSchema: external_exports.object({
19810
20165
  action: external_exports.enum([
19811
20166
  "capture",
@@ -19829,7 +20184,11 @@ Output formats: full (default, includes content), paths (file paths only - 80% t
19829
20184
  // Team actions (team plans only)
19830
20185
  "team_decisions",
19831
20186
  "team_lessons",
19832
- "team_plans"
20187
+ "team_plans",
20188
+ // Suggested rules actions (ML-generated)
20189
+ "list_suggested_rules",
20190
+ "suggested_rule_action",
20191
+ "suggested_rules_stats"
19833
20192
  ]).describe("Action to perform"),
19834
20193
  workspace_id: external_exports.string().uuid().optional(),
19835
20194
  project_id: external_exports.string().uuid().optional(),
@@ -19906,7 +20265,13 @@ Output formats: full (default, includes content), paths (file paths only - 80% t
19906
20265
  is_personal: external_exports.boolean().optional().describe("Mark plan as personal (only visible to creator). For capture_plan/list_plans."),
19907
20266
  // Restore context params
19908
20267
  snapshot_id: external_exports.string().uuid().optional().describe("Specific snapshot ID to restore (defaults to most recent)"),
19909
- max_snapshots: external_exports.number().optional().default(1).describe("Number of recent snapshots to consider (default: 1)")
20268
+ max_snapshots: external_exports.number().optional().default(1).describe("Number of recent snapshots to consider (default: 1)"),
20269
+ // Suggested rules params
20270
+ rule_id: external_exports.string().uuid().optional().describe("Suggested rule ID for actions"),
20271
+ rule_action: external_exports.enum(["accept", "reject", "modify"]).optional().describe("Action to perform on suggested rule"),
20272
+ modified_keywords: external_exports.array(external_exports.string()).optional().describe("Modified keywords when action is modify"),
20273
+ modified_instruction: external_exports.string().optional().describe("Modified instruction when action is modify"),
20274
+ min_confidence: external_exports.number().optional().describe("Minimum confidence threshold for listing rules")
19910
20275
  })
19911
20276
  },
19912
20277
  async (input) => {
@@ -19929,8 +20294,10 @@ Output formats: full (default, includes content), paths (file paths only - 80% t
19929
20294
  code_refs: input.code_refs,
19930
20295
  provenance: input.provenance
19931
20296
  });
20297
+ const captureHint = getCaptureHint(input.event_type);
20298
+ const resultWithHint = { ...result, hint: captureHint };
19932
20299
  return {
19933
- content: [{ type: "text", text: formatContent(result) }]
20300
+ content: [{ type: "text", text: formatContent(resultWithHint) }]
19934
20301
  };
19935
20302
  }
19936
20303
  case "capture_lesson": {
@@ -19986,8 +20353,10 @@ Output formats: full (default, includes content), paths (file paths only - 80% t
19986
20353
  importance: input.severity === "critical" ? "critical" : input.severity === "high" ? "high" : "medium",
19987
20354
  tags: input.keywords || []
19988
20355
  });
20356
+ const lessonHint = getCaptureHint("lesson");
20357
+ const resultWithHint = { ...result, hint: lessonHint };
19989
20358
  return {
19990
- content: [{ type: "text", text: formatContent(result) }]
20359
+ content: [{ type: "text", text: formatContent(resultWithHint) }]
19991
20360
  };
19992
20361
  }
19993
20362
  case "get_lessons": {
@@ -20000,8 +20369,10 @@ Output formats: full (default, includes content), paths (file paths only - 80% t
20000
20369
  context_hint: input.query,
20001
20370
  limit: input.limit
20002
20371
  });
20372
+ const lessons = result?.data?.lessons || result?.lessons || [];
20373
+ const resultWithHint = Array.isArray(lessons) && lessons.length === 0 ? { ...result, hint: getEmptyStateHint("get_lessons") } : result;
20003
20374
  return {
20004
- content: [{ type: "text", text: formatContent(result) }]
20375
+ content: [{ type: "text", text: formatContent(resultWithHint) }]
20005
20376
  };
20006
20377
  }
20007
20378
  case "recall": {
@@ -20015,7 +20386,9 @@ Output formats: full (default, includes content), paths (file paths only - 80% t
20015
20386
  include_related: input.include_related,
20016
20387
  include_decisions: input.include_decisions
20017
20388
  });
20018
- const outputText = formatContent(result);
20389
+ const recallResults = result?.data?.results || result?.results || [];
20390
+ const recallWithHint = Array.isArray(recallResults) && recallResults.length === 0 ? { ...result, hint: getEmptyStateHint("recall") } : result;
20391
+ const outputText = formatContent(recallWithHint);
20019
20392
  trackToolTokenSavings(client, "session_recall", outputText, {
20020
20393
  workspace_id: workspaceId,
20021
20394
  project_id: projectId
@@ -20035,8 +20408,10 @@ Output formats: full (default, includes content), paths (file paths only - 80% t
20035
20408
  content: input.content,
20036
20409
  importance
20037
20410
  });
20411
+ const rememberHint = getCaptureHint("preference");
20412
+ const resultWithHint = { ...result, hint: rememberHint };
20038
20413
  return {
20039
- content: [{ type: "text", text: formatContent(result) }]
20414
+ content: [{ type: "text", text: formatContent(resultWithHint) }]
20040
20415
  };
20041
20416
  }
20042
20417
  case "user_context": {
@@ -20149,8 +20524,10 @@ Output formats: full (default, includes content), paths (file paths only - 80% t
20149
20524
  source_tool: input.source_tool || "mcp",
20150
20525
  is_personal: input.is_personal
20151
20526
  });
20527
+ const planHint = getPlanStatusHint(input.status || "draft");
20528
+ const resultWithHint = { ...result, hint: planHint };
20152
20529
  return {
20153
- content: [{ type: "text", text: formatContent(result) }]
20530
+ content: [{ type: "text", text: formatContent(resultWithHint) }]
20154
20531
  };
20155
20532
  }
20156
20533
  case "get_plan": {
@@ -20180,8 +20557,10 @@ Output formats: full (default, includes content), paths (file paths only - 80% t
20180
20557
  tags: input.tags,
20181
20558
  due_at: input.due_at
20182
20559
  });
20560
+ const planUpdateHint = input.status ? getPlanStatusHint(input.status) : "Plan updated. Changes are preserved.";
20561
+ const resultWithHint = { ...result, hint: planUpdateHint };
20183
20562
  return {
20184
- content: [{ type: "text", text: formatContent(result) }]
20563
+ content: [{ type: "text", text: formatContent(resultWithHint) }]
20185
20564
  };
20186
20565
  }
20187
20566
  case "list_plans": {
@@ -20195,8 +20574,10 @@ Output formats: full (default, includes content), paths (file paths only - 80% t
20195
20574
  limit: input.limit,
20196
20575
  is_personal: input.is_personal
20197
20576
  });
20577
+ const plans = result?.data?.plans || result?.plans || result?.data?.items || result?.items || [];
20578
+ const resultWithHint = plans.length === 0 ? { ...result, hint: getEmptyStateHint("list_plans") } : result;
20198
20579
  return {
20199
- content: [{ type: "text", text: formatContent(result) }]
20580
+ content: [{ type: "text", text: formatContent(resultWithHint) }]
20200
20581
  };
20201
20582
  }
20202
20583
  case "restore_context": {
@@ -20466,6 +20847,81 @@ Output formats: full (default, includes content), paths (file paths only - 80% t
20466
20847
  ]
20467
20848
  };
20468
20849
  }
20850
+ case "list_suggested_rules": {
20851
+ const result = await client.listSuggestedRules({
20852
+ workspace_id: workspaceId,
20853
+ status: input.status,
20854
+ min_confidence: input.min_confidence,
20855
+ limit: input.limit
20856
+ });
20857
+ const rules = result?.data?.items || result?.items || [];
20858
+ if (rules.length === 0) {
20859
+ return {
20860
+ content: [
20861
+ {
20862
+ type: "text",
20863
+ text: formatContent({
20864
+ suggested_rules: [],
20865
+ hint: "No pending rule suggestions. The ML system learns from your lessons and will suggest rules when patterns are detected."
20866
+ })
20867
+ }
20868
+ ]
20869
+ };
20870
+ }
20871
+ return {
20872
+ content: [
20873
+ {
20874
+ type: "text",
20875
+ text: formatContent({
20876
+ suggested_rules: rules,
20877
+ total: result?.data?.total || rules.length,
20878
+ hint: "Use suggested_rule_action to accept, reject, or modify these suggestions."
20879
+ })
20880
+ }
20881
+ ]
20882
+ };
20883
+ }
20884
+ case "suggested_rule_action": {
20885
+ if (!input.rule_id || !input.rule_action) {
20886
+ return errorResult("suggested_rule_action requires: rule_id, rule_action (accept/reject/modify)");
20887
+ }
20888
+ const result = await client.suggestedRuleAction({
20889
+ rule_id: input.rule_id,
20890
+ action: input.rule_action,
20891
+ modified_keywords: input.modified_keywords,
20892
+ modified_instruction: input.modified_instruction
20893
+ });
20894
+ const actionVerb = input.rule_action === "accept" ? "accepted" : input.rule_action === "reject" ? "rejected" : "modified";
20895
+ return {
20896
+ content: [
20897
+ {
20898
+ type: "text",
20899
+ text: formatContent({
20900
+ success: true,
20901
+ message: `Rule ${actionVerb} successfully`,
20902
+ rule: result?.data || result,
20903
+ hint: input.rule_action === "accept" ? "This rule will now be applied to future context() calls." : input.rule_action === "reject" ? "This pattern will have reduced confidence for future suggestions." : "The modified rule will be applied to future context() calls."
20904
+ })
20905
+ }
20906
+ ]
20907
+ };
20908
+ }
20909
+ case "suggested_rules_stats": {
20910
+ const result = await client.getSuggestedRulesStats({
20911
+ workspace_id: workspaceId
20912
+ });
20913
+ return {
20914
+ content: [
20915
+ {
20916
+ type: "text",
20917
+ text: formatContent({
20918
+ stats: result?.data || result,
20919
+ hint: "These stats show ML vs Grok accuracy. The blend weight auto-adjusts based on these metrics."
20920
+ })
20921
+ }
20922
+ ]
20923
+ };
20924
+ }
20469
20925
  default:
20470
20926
  return errorResult(`Unknown action: ${input.action}`);
20471
20927
  }
@@ -20475,7 +20931,7 @@ Output formats: full (default, includes content), paths (file paths only - 80% t
20475
20931
  "memory",
20476
20932
  {
20477
20933
  title: "Memory",
20478
- description: `Memory operations for events and nodes. Event actions: create_event, get_event, update_event, delete_event, list_events, distill_event, import_batch (bulk import array of events). Node actions: create_node, get_node, update_node, delete_node, list_nodes, supersede_node. Query actions: search, decisions, timeline, summary. Task actions: create_task (create task, optionally linked to plan), get_task, update_task (can link/unlink task to plan via plan_id), delete_task, list_tasks, reorder_tasks. Todo actions: create_todo, list_todos, get_todo, update_todo, delete_todo, complete_todo. Diagram actions: create_diagram, list_diagrams, get_diagram, update_diagram, delete_diagram. Doc actions: create_doc, list_docs, get_doc, update_doc, delete_doc, create_roadmap. Team actions (team plans only): team_tasks, team_todos, team_diagrams, team_docs.`,
20934
+ description: `Memory operations for events and nodes. Event actions: create_event, get_event, update_event, delete_event, list_events, distill_event, import_batch (bulk import array of events). Node actions: create_node, get_node, update_node, delete_node, list_nodes, supersede_node. Query actions: search, decisions, timeline, summary. Task actions: create_task (create task, optionally linked to plan), get_task, update_task (can link/unlink task to plan via plan_id), delete_task, list_tasks, reorder_tasks. Todo actions: create_todo, list_todos, get_todo, update_todo, delete_todo, complete_todo. Diagram actions: create_diagram, list_diagrams, get_diagram, update_diagram, delete_diagram. Doc actions: create_doc, list_docs, get_doc, update_doc, delete_doc, create_roadmap. Transcript actions: list_transcripts (list saved conversations), get_transcript (get full transcript by ID), search_transcripts (semantic search across conversations), delete_transcript. Team actions (team plans only): team_tasks, team_todos, team_diagrams, team_docs.`,
20479
20935
  inputSchema: external_exports.object({
20480
20936
  action: external_exports.enum([
20481
20937
  "create_event",
@@ -20523,6 +20979,11 @@ Output formats: full (default, includes content), paths (file paths only - 80% t
20523
20979
  "update_doc",
20524
20980
  "delete_doc",
20525
20981
  "create_roadmap",
20982
+ // Transcript actions
20983
+ "list_transcripts",
20984
+ "get_transcript",
20985
+ "search_transcripts",
20986
+ "delete_transcript",
20526
20987
  // Team actions
20527
20988
  "team_tasks",
20528
20989
  "team_todos",
@@ -20628,7 +21089,13 @@ Output formats: full (default, includes content), paths (file paths only - 80% t
20628
21089
  })
20629
21090
  ).optional().describe("Milestones for create_roadmap action"),
20630
21091
  // Personal items param
20631
- is_personal: external_exports.boolean().optional().describe("Mark as personal (only visible to creator). For create/list actions on todos, diagrams, docs.")
21092
+ is_personal: external_exports.boolean().optional().describe("Mark as personal (only visible to creator). For create/list actions on todos, diagrams, docs."),
21093
+ // Transcript params
21094
+ transcript_id: external_exports.string().uuid().optional().describe("Transcript ID for get_transcript/delete_transcript"),
21095
+ session_id: external_exports.string().optional().describe("Session ID filter for list_transcripts"),
21096
+ client_name: external_exports.string().optional().describe("Client name filter for list_transcripts (e.g., 'claude', 'cursor')"),
21097
+ started_after: external_exports.string().optional().describe("ISO timestamp - filter transcripts started after this time"),
21098
+ started_before: external_exports.string().optional().describe("ISO timestamp - filter transcripts started before this time")
20632
21099
  })
20633
21100
  },
20634
21101
  async (input) => {
@@ -20690,8 +21157,10 @@ Output formats: full (default, includes content), paths (file paths only - 80% t
20690
21157
  project_id: projectId,
20691
21158
  limit: input.limit
20692
21159
  });
21160
+ const events = result?.data?.items || result?.items || result?.data || [];
21161
+ const resultWithHint = Array.isArray(events) && events.length === 0 ? { ...result, hint: getEmptyStateHint("list_events") } : result;
20693
21162
  return {
20694
- content: [{ type: "text", text: formatContent(result) }]
21163
+ content: [{ type: "text", text: formatContent(resultWithHint) }]
20695
21164
  };
20696
21165
  }
20697
21166
  case "import_batch": {
@@ -20884,8 +21353,10 @@ ${formatContent(result)}`
20884
21353
  tags: input.tags,
20885
21354
  is_personal: input.is_personal
20886
21355
  });
21356
+ const taskCreateHint = TASK_HINTS.created;
21357
+ const resultWithHint = { ...result, hint: taskCreateHint };
20887
21358
  return {
20888
- content: [{ type: "text", text: formatContent(result) }]
21359
+ content: [{ type: "text", text: formatContent(resultWithHint) }]
20889
21360
  };
20890
21361
  }
20891
21362
  case "get_task": {
@@ -20917,8 +21388,17 @@ ${formatContent(result)}`
20917
21388
  tags: input.tags,
20918
21389
  blocked_reason: input.blocked_reason
20919
21390
  });
21391
+ let taskUpdateHint = "Task updated.";
21392
+ if (input.task_status === "completed") {
21393
+ taskUpdateHint = TASK_HINTS.completed;
21394
+ } else if (input.task_status === "blocked") {
21395
+ taskUpdateHint = TASK_HINTS.blocked;
21396
+ } else if (input.task_status === "cancelled") {
21397
+ taskUpdateHint = TASK_HINTS.cancelled;
21398
+ }
21399
+ const resultWithHint = { ...result, hint: taskUpdateHint };
20920
21400
  return {
20921
- content: [{ type: "text", text: formatContent(result) }]
21401
+ content: [{ type: "text", text: formatContent(resultWithHint) }]
20922
21402
  };
20923
21403
  }
20924
21404
  case "delete_task": {
@@ -20945,8 +21425,10 @@ ${formatContent(result)}`
20945
21425
  limit: input.limit,
20946
21426
  is_personal: input.is_personal
20947
21427
  });
21428
+ const tasks = result?.data?.tasks || result?.tasks || result?.data?.items || result?.items || [];
21429
+ const resultWithHint = Array.isArray(tasks) && tasks.length === 0 ? { ...result, hint: getEmptyStateHint("list_tasks") } : result;
20948
21430
  return {
20949
- content: [{ type: "text", text: formatContent(result) }]
21431
+ content: [{ type: "text", text: formatContent(resultWithHint) }]
20950
21432
  };
20951
21433
  }
20952
21434
  case "reorder_tasks": {
@@ -20998,8 +21480,10 @@ ${formatContent(result)}`
20998
21480
  priority: input.todo_priority,
20999
21481
  is_personal: input.is_personal
21000
21482
  });
21483
+ const todos = todosResult?.data?.todos || todosResult?.todos || todosResult?.data?.items || todosResult?.items || [];
21484
+ const todosWithHint = Array.isArray(todos) && todos.length === 0 ? { ...todosResult, hint: getEmptyStateHint("list_todos") } : todosResult;
21001
21485
  return {
21002
- content: [{ type: "text", text: formatContent(todosResult) }]
21486
+ content: [{ type: "text", text: formatContent(todosWithHint) }]
21003
21487
  };
21004
21488
  }
21005
21489
  case "get_todo": {
@@ -21075,8 +21559,10 @@ ${formatContent(result)}`
21075
21559
  diagram_type: input.diagram_type,
21076
21560
  is_personal: input.is_personal
21077
21561
  });
21562
+ const diagrams = diagramsResult?.data?.diagrams || diagramsResult?.diagrams || diagramsResult?.data?.items || diagramsResult?.items || [];
21563
+ const diagramsWithHint = Array.isArray(diagrams) && diagrams.length === 0 ? { ...diagramsResult, hint: getEmptyStateHint("list_diagrams") } : diagramsResult;
21078
21564
  return {
21079
- content: [{ type: "text", text: formatContent(diagramsResult) }]
21565
+ content: [{ type: "text", text: formatContent(diagramsWithHint) }]
21080
21566
  };
21081
21567
  }
21082
21568
  case "get_diagram": {
@@ -21149,8 +21635,10 @@ ${formatContent(result)}`
21149
21635
  doc_type: input.doc_type,
21150
21636
  is_personal: input.is_personal
21151
21637
  });
21638
+ const docs = docsResult?.data?.docs || docsResult?.docs || docsResult?.data?.items || docsResult?.items || [];
21639
+ const docsWithHint = Array.isArray(docs) && docs.length === 0 ? { ...docsResult, hint: getEmptyStateHint("list_docs") } : docsResult;
21152
21640
  return {
21153
- content: [{ type: "text", text: formatContent(docsResult) }]
21641
+ content: [{ type: "text", text: formatContent(docsWithHint) }]
21154
21642
  };
21155
21643
  }
21156
21644
  case "get_doc": {
@@ -21381,6 +21869,54 @@ ${formatContent(result)}`
21381
21869
  ]
21382
21870
  };
21383
21871
  }
21872
+ // Transcript actions
21873
+ case "list_transcripts": {
21874
+ const result = await client.listTranscripts({
21875
+ workspace_id: workspaceId,
21876
+ project_id: projectId,
21877
+ session_id: input.session_id,
21878
+ client_name: input.client_name,
21879
+ started_after: input.started_after,
21880
+ started_before: input.started_before,
21881
+ limit: input.limit
21882
+ });
21883
+ const transcripts = result?.data?.items || result?.items || result?.data || [];
21884
+ const resultWithHint = Array.isArray(transcripts) && transcripts.length === 0 ? { ...result, hint: "No transcripts found. Enable save_exchange in context() calls to save conversations." } : result;
21885
+ return {
21886
+ content: [{ type: "text", text: formatContent(resultWithHint) }]
21887
+ };
21888
+ }
21889
+ case "get_transcript": {
21890
+ if (!input.transcript_id) {
21891
+ return errorResult("get_transcript requires: transcript_id");
21892
+ }
21893
+ const result = await client.getTranscript(input.transcript_id);
21894
+ return {
21895
+ content: [{ type: "text", text: formatContent(result) }]
21896
+ };
21897
+ }
21898
+ case "search_transcripts": {
21899
+ if (!input.query) {
21900
+ return errorResult("search_transcripts requires: query");
21901
+ }
21902
+ const result = await client.searchTranscripts({
21903
+ workspace_id: workspaceId,
21904
+ query: input.query,
21905
+ limit: input.limit
21906
+ });
21907
+ return {
21908
+ content: [{ type: "text", text: formatContent(result) }]
21909
+ };
21910
+ }
21911
+ case "delete_transcript": {
21912
+ if (!input.transcript_id) {
21913
+ return errorResult("delete_transcript requires: transcript_id");
21914
+ }
21915
+ const result = await client.deleteTranscript(input.transcript_id);
21916
+ return {
21917
+ content: [{ type: "text", text: formatContent(result) }]
21918
+ };
21919
+ }
21384
21920
  default:
21385
21921
  return errorResult(`Unknown action: ${input.action}`);
21386
21922
  }
@@ -22669,6 +23205,8 @@ Example workflow:
22669
23205
  const projectId = resolveProjectId(input.project_id);
22670
23206
  switch (input.action) {
22671
23207
  case "index": {
23208
+ const indexGate = await gateIfProTool("media_index");
23209
+ if (indexGate) return indexGate;
22672
23210
  if (!input.file_path && !input.external_url) {
22673
23211
  return errorResult("index requires: file_path or external_url");
22674
23212
  }
@@ -22833,6 +23371,8 @@ Created: ${content.created_at}`
22833
23371
  }
22834
23372
  }
22835
23373
  case "search": {
23374
+ const searchGate = await gateIfProTool("media_search");
23375
+ if (searchGate) return searchGate;
22836
23376
  if (!input.query) {
22837
23377
  return errorResult("search requires: query");
22838
23378
  }
@@ -23048,14 +23588,14 @@ Content ID: ${input.content_id}`
23048
23588
  // For editor_rules
23049
23589
  folder_path: external_exports.string().optional(),
23050
23590
  editors: external_exports.array(external_exports.string()).optional(),
23051
- mode: external_exports.enum(["minimal", "full"]).optional(),
23591
+ mode: external_exports.enum(["minimal", "full", "bootstrap"]).optional(),
23052
23592
  dry_run: external_exports.boolean().optional(),
23053
23593
  workspace_id: external_exports.string().uuid().optional(),
23054
23594
  workspace_name: external_exports.string().optional(),
23055
23595
  project_name: external_exports.string().optional(),
23056
23596
  additional_rules: external_exports.string().optional(),
23057
23597
  install_hooks: external_exports.boolean().optional().describe("Install Claude Code hooks (PreToolUse, UserPromptSubmit, PostToolUse). Default: true for Claude users."),
23058
- include_pre_compact: external_exports.boolean().optional().describe("Include PreCompact hook for auto-saving state before compaction. Default: false."),
23598
+ include_pre_compact: external_exports.boolean().optional().describe("Include PreCompact hook for auto-saving state before compaction. Default: true."),
23059
23599
  include_post_write: external_exports.boolean().optional().describe("Include PostToolUse hook for real-time file indexing after Edit/Write operations. Default: true."),
23060
23600
  // For enable_bundle
23061
23601
  bundle: external_exports.enum([
@@ -23112,7 +23652,7 @@ Each domain tool has an 'action' parameter for specific operations.` : "";
23112
23652
  scope: input.folder_path ? "both" : "user",
23113
23653
  projectPath: input.folder_path,
23114
23654
  dryRun: input.dry_run,
23115
- includePreCompact: input.include_pre_compact,
23655
+ includePreCompact: input.include_pre_compact !== false,
23116
23656
  includePostWrite: input.include_post_write
23117
23657
  });
23118
23658
  } catch (err) {
@@ -24053,6 +24593,7 @@ function registerPrompts(server) {
24053
24593
  }
24054
24594
 
24055
24595
  // src/session-manager.ts
24596
+ import { randomUUID as randomUUID2 } from "crypto";
24056
24597
  var SessionManager = class _SessionManager {
24057
24598
  constructor(server, client) {
24058
24599
  this.server = server;
@@ -24084,12 +24625,20 @@ var SessionManager = class _SessionManager {
24084
24625
  this.lastHighPressureAt = null;
24085
24626
  this.lastHighPressureTokens = 0;
24086
24627
  this.postCompactRestoreCompleted = false;
24628
+ this.sessionId = `mcp-${randomUUID2()}`;
24087
24629
  }
24088
24630
  static {
24089
24631
  // Each conversation turn typically includes: user message (~500), AI response (~1500),
24090
24632
  // system prompt overhead (~500), and reasoning (~1500). Conservative estimate: 3000/turn
24091
24633
  this.TOKENS_PER_TURN_ESTIMATE = 3e3;
24092
24634
  }
24635
+ /**
24636
+ * Get the unique session ID for this MCP connection.
24637
+ * Used for transcript saving and session association.
24638
+ */
24639
+ getSessionId() {
24640
+ return this.sessionId;
24641
+ }
24093
24642
  /**
24094
24643
  * Check if session has been auto-initialized
24095
24644
  */
@@ -24624,7 +25173,7 @@ var SessionManager = class _SessionManager {
24624
25173
 
24625
25174
  // src/http-gateway.ts
24626
25175
  import { createServer } from "node:http";
24627
- import { randomUUID as randomUUID2 } from "node:crypto";
25176
+ import { randomUUID as randomUUID3 } from "node:crypto";
24628
25177
  import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
24629
25178
  import { StreamableHTTPServerTransport } from "@modelcontextprotocol/sdk/server/streamableHttp.js";
24630
25179
  var HOST = process.env.MCP_HTTP_HOST || "0.0.0.0";
@@ -24801,7 +25350,7 @@ async function createSession() {
24801
25350
  registerPrompts(server);
24802
25351
  }
24803
25352
  const transport = new StreamableHTTPServerTransport({
24804
- sessionIdGenerator: () => randomUUID2(),
25353
+ sessionIdGenerator: () => randomUUID3(),
24805
25354
  enableJsonResponse: ENABLE_JSON_RESPONSE,
24806
25355
  onsessionclosed: (sessionId) => {
24807
25356
  sessions.delete(sessionId);
@@ -25845,7 +26394,7 @@ Code: ${device.user_code}`);
25845
26394
  }
25846
26395
  }
25847
26396
  }
25848
- const mode = "dynamic";
26397
+ const mode = "full";
25849
26398
  const detectedPlanName = await client.getPlanName();
25850
26399
  const detectedGraphTier = await client.getGraphTier();
25851
26400
  const graphTierLabel = detectedGraphTier === "full" ? "full graph" : detectedGraphTier === "lite" ? "graph-lite" : "none";