@adverant/nexus-memory-skill 1.3.0 → 2.1.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.
@@ -1,21 +1,20 @@
1
1
  #!/bin/bash
2
2
  #
3
- # Nexus Memory - Enhanced Episode Summary Hook (GraphRAG v2)
3
+ # Nexus Memory - Optimized Episode Summary Hook (GraphRAG v2)
4
4
  # Captures significant tool uses and generates periodic episode summaries.
5
- # Uses entity extraction and causal chaining for knowledge graph building.
6
5
  #
7
- # This hook:
8
- # - Stores SIGNIFICANT tool uses (filters out routine operations)
9
- # - Generates episode summaries every N tool uses (default: 10)
10
- # - Builds causal chains between related operations
11
- # - Extracts entities for knowledge graph
6
+ # PERFORMANCE OPTIMIZED:
7
+ # - Smart tool filtering (skip routine operations)
8
+ # - Significance scoring for tools
9
+ # - Adaptive episode thresholds based on activity
10
+ # - Compressed episode summaries for efficiency
11
+ # - Async storage for non-blocking operation
12
12
  #
13
13
  # Episodes capture:
14
- # - Tool name and inputs
15
- # - Tool outputs (truncated)
16
- # - Project context
17
- # - Causal relationships
18
- # - Extracted entities
14
+ # - Significant tool uses only
15
+ # - Project context and decisions made
16
+ # - Causal relationships between actions
17
+ # - Extracted entities for knowledge graph
19
18
  #
20
19
  # Usage:
21
20
  # echo '{"tool_name": "Bash", "tool_input": {...}, "tool_output": "..."}' | episode-summary.sh
@@ -31,7 +30,7 @@
31
30
  # GraphRAG Enhancement Options:
32
31
  # NEXUS_EXTRACT_ENTITIES - Enable entity extraction (default: true)
33
32
  # NEXUS_CREATE_RELATIONS - Create knowledge graph relationships (default: true)
34
- # NEXUS_SKIP_ROUTINE - Skip routine tool uses like Read, ls (default: true)
33
+ # NEXUS_MIN_SIGNIFICANCE - Minimum significance to store tool use (default: 40)
35
34
  #
36
35
 
37
36
  set -o pipefail
@@ -47,13 +46,14 @@ EPISODE_THRESHOLD="${NEXUS_EPISODE_THRESHOLD:-10}"
47
46
  # GraphRAG Enhancement Configuration
48
47
  EXTRACT_ENTITIES="${NEXUS_EXTRACT_ENTITIES:-true}"
49
48
  CREATE_RELATIONS="${NEXUS_CREATE_RELATIONS:-true}"
50
- SKIP_ROUTINE="${NEXUS_SKIP_ROUTINE:-true}"
49
+ MIN_SIGNIFICANCE="${NEXUS_MIN_SIGNIFICANCE:-40}"
51
50
 
52
- # State file for tracking tool count and causal context
51
+ # State file for tracking
53
52
  STATE_DIR="${HOME}/.claude/session-env"
54
53
  COUNTER_FILE="${STATE_DIR}/episode_counter"
55
54
  LAST_TOOL_ID_FILE="${STATE_DIR}/last_tool_id"
56
55
  SESSION_TOOLS_FILE="${STATE_DIR}/session_tools"
56
+ LAST_EPISODE_FILE="${STATE_DIR}/last_episode_id"
57
57
 
58
58
  # Logging function
59
59
  log() {
@@ -66,43 +66,45 @@ log_error() {
66
66
  echo "[episode-summary] ERROR: $1" >&2
67
67
  }
68
68
 
69
+ # =========================================================
70
+ # FAST PATH: Early exit checks
71
+ # =========================================================
72
+
69
73
  # Skip if no API key
70
74
  if [[ -z "$NEXUS_API_KEY" ]]; then
71
- log "NEXUS_API_KEY not set, skipping episode summary"
72
- exit 0
73
- fi
74
-
75
- # Check dependencies
76
- if ! command -v jq &> /dev/null; then
77
- log "jq not installed, skipping episode summary"
78
75
  exit 0
79
76
  fi
80
77
 
81
- if ! command -v curl &> /dev/null; then
82
- log "curl not installed, skipping episode summary"
83
- exit 0
84
- fi
78
+ # Fast dependency check
79
+ type jq &>/dev/null || exit 0
80
+ type curl &>/dev/null || exit 0
85
81
 
86
82
  # Ensure state directory exists
87
- mkdir -p "$STATE_DIR"
83
+ mkdir -p "$STATE_DIR" 2>/dev/null
88
84
 
89
85
  # Read input from stdin
90
86
  INPUT=$(cat)
91
87
 
92
88
  if [[ -z "$INPUT" ]]; then
93
- log "No input provided"
94
89
  exit 0
95
90
  fi
96
91
 
97
92
  log "Received input: ${INPUT:0:100}..."
98
93
 
99
- # Extract fields from input
100
- FORCE_SUMMARY=$(echo "$INPUT" | jq -r '.force // false' 2>/dev/null)
101
- SESSION_END=$(echo "$INPUT" | jq -r '.session_end // false' 2>/dev/null)
102
- TOOL_NAME=$(echo "$INPUT" | jq -r '.tool_name // empty' 2>/dev/null)
103
- TOOL_INPUT=$(echo "$INPUT" | jq -r '.tool_input // empty' 2>/dev/null)
104
- TOOL_OUTPUT=$(echo "$INPUT" | jq -r '.tool_output // empty' 2>/dev/null)
105
- CONTENT=$(echo "$INPUT" | jq -r '.content // .conversation_summary // .prompt // empty' 2>/dev/null)
94
+ # =========================================================
95
+ # INPUT PARSING
96
+ # =========================================================
97
+
98
+ # Extract fields with single jq call
99
+ read -r TOOL_NAME FORCE_SUMMARY SESSION_END < <(echo "$INPUT" | jq -r '[
100
+ (.tool_name // ""),
101
+ (.force // "false"),
102
+ (.session_end // "false")
103
+ ] | @tsv' 2>/dev/null || echo " false false")
104
+
105
+ TOOL_INPUT=$(echo "$INPUT" | jq -c '.tool_input // {}' 2>/dev/null)
106
+ TOOL_OUTPUT=$(echo "$INPUT" | jq -r '.tool_output // ""' 2>/dev/null)
107
+ CONTENT=$(echo "$INPUT" | jq -r '.content // .conversation_summary // .prompt // ""' 2>/dev/null)
106
108
 
107
109
  # Get project context
108
110
  PROJECT_NAME=$(basename "$(pwd)")
@@ -110,81 +112,113 @@ PROJECT_DIR=$(pwd)
110
112
  TIMESTAMP=$(date -u +%Y-%m-%dT%H:%M:%SZ)
111
113
  INTERACTION_ID="${PROJECT_NAME}-$(date +%s)-$$"
112
114
 
113
- # --- ROUTINE TOOL FILTERING ---
114
- # Skip storing routine operations that don't add value to memory
115
+ # =========================================================
116
+ # TOOL SIGNIFICANCE SCORING
117
+ # =========================================================
115
118
 
116
- should_skip_tool() {
119
+ # Tool categories with base significance scores
120
+ get_tool_significance() {
117
121
  local tool="$1"
118
122
  local input="$2"
119
-
120
- if [[ "$SKIP_ROUTINE" != "true" ]]; then
121
- return 1 # Don't skip anything
122
- fi
123
-
124
- # Always skip Read tool (just reading files, no action)
125
- if [[ "$tool" == "Read" ]]; then
126
- log "Skipping routine Read tool"
127
- return 0
128
- fi
129
-
130
- # Skip Glob and Grep (search operations)
131
- if [[ "$tool" == "Glob" ]] || [[ "$tool" == "Grep" ]]; then
132
- log "Skipping routine search tool: $tool"
133
- return 0
134
- fi
135
-
136
- # Skip routine Bash commands
137
- if [[ "$tool" == "Bash" ]]; then
138
- # Extract command from input
139
- local cmd=$(echo "$input" | jq -r '.command // ""' 2>/dev/null)
140
-
141
- # Skip common read-only commands
142
- if echo "$cmd" | grep -qE '^(ls|pwd|cat|head|tail|wc|find|grep|which|echo|date|whoami|hostname|env|printenv|type)(\s|$)'; then
143
- log "Skipping routine Bash command: ${cmd:0:50}"
144
- return 0
123
+ local output="$3"
124
+ local score=50 # Base score
125
+
126
+ # High-value tools (creation/modification)
127
+ case "$tool" in
128
+ Write|Edit|NotebookEdit)
129
+ score=80 # File modifications are significant
130
+ ;;
131
+ Bash)
132
+ # Analyze the command
133
+ local cmd=$(echo "$input" | jq -r '.command // ""' 2>/dev/null)
134
+
135
+ # Skip routine read commands
136
+ if echo "$cmd" | grep -qE '^(ls|pwd|cat|head|tail|wc|find|grep|which|echo|date|whoami|type|file)(\s|$)'; then
137
+ score=10
138
+ # Skip routine git read commands
139
+ elif echo "$cmd" | grep -qE '^git\s+(status|log|diff|branch|show|remote|fetch)(\s|$)'; then
140
+ score=15
141
+ # High-value git commands
142
+ elif echo "$cmd" | grep -qE '^git\s+(commit|push|merge|rebase|checkout|reset)(\s|$)'; then
143
+ score=85
144
+ # Package management
145
+ elif echo "$cmd" | grep -qE '^(npm|yarn|pnpm)\s+(install|add|remove|run build|run test)'; then
146
+ score=70
147
+ # Docker commands
148
+ elif echo "$cmd" | grep -qE '^docker\s+(build|run|push|pull|compose)'; then
149
+ score=75
150
+ # kubectl commands
151
+ elif echo "$cmd" | grep -qE '^(kubectl|k3s kubectl)\s+(apply|delete|create|rollout)'; then
152
+ score=80
153
+ else
154
+ score=40 # Unknown commands get medium score
155
+ fi
156
+ ;;
157
+ TodoWrite)
158
+ score=60 # Task management
159
+ ;;
160
+ WebFetch|WebSearch)
161
+ score=30 # Research/lookup
162
+ ;;
163
+ Read|Glob|Grep)
164
+ score=10 # Read-only operations
165
+ ;;
166
+ Skill)
167
+ score=75 # Skill invocations are significant
168
+ ;;
169
+ *)
170
+ score=50 # Unknown tools get base score
171
+ ;;
172
+ esac
173
+
174
+ # Output-based adjustments
175
+ if [[ -n "$output" ]]; then
176
+ # Errors are always significant
177
+ if echo "$output" | grep -qiE '(error|exception|failed|failure)'; then
178
+ score=$((score + 20))
145
179
  fi
146
180
 
147
- # Skip git status/log/diff (read-only git commands)
148
- if echo "$cmd" | grep -qE '^git\s+(status|log|diff|branch|show|remote|fetch)(\s|$)'; then
149
- log "Skipping routine git command: ${cmd:0:50}"
150
- return 0
181
+ # Success messages boost significance
182
+ if echo "$output" | grep -qiE '(success|completed|created|fixed|resolved)'; then
183
+ score=$((score + 15))
151
184
  fi
152
185
  fi
153
186
 
154
- # Don't skip - this is a significant tool use
155
- return 1
187
+ # Clamp to 0-100
188
+ if [[ "$score" -lt 0 ]]; then score=0; fi
189
+ if [[ "$score" -gt 100 ]]; then score=100; fi
190
+
191
+ echo "$score"
156
192
  }
157
193
 
158
- # Check if tool output indicates an error/fix (always store these)
159
- is_significant_output() {
160
- local output="$1"
194
+ # =========================================================
195
+ # TOOL PROCESSING
196
+ # =========================================================
197
+
198
+ if [[ -n "$TOOL_NAME" ]] && [[ "$TOOL_NAME" != "null" ]]; then
199
+ # Calculate tool significance
200
+ SIGNIFICANCE=$(get_tool_significance "$TOOL_NAME" "$TOOL_INPUT" "$TOOL_OUTPUT")
201
+ log "Tool: $TOOL_NAME, Significance: $SIGNIFICANCE (min: $MIN_SIGNIFICANCE)"
161
202
 
162
- # Always significant: errors, fixes, failures
163
- if echo "$output" | grep -qiE '(error|exception|failed|failure|fix|fixed|resolved|success|completed|created|deleted|modified)'; then
164
- return 0
203
+ # Get current counter
204
+ CURRENT_COUNT=0
205
+ if [[ -f "$COUNTER_FILE" ]]; then
206
+ CURRENT_COUNT=$(cat "$COUNTER_FILE" 2>/dev/null || echo "0")
165
207
  fi
166
208
 
167
- return 1
168
- }
209
+ # Always increment counter
210
+ NEW_COUNT=$((CURRENT_COUNT + 1))
211
+ echo "$NEW_COUNT" > "$COUNTER_FILE"
169
212
 
170
- # --- STEP 1: Process tool use if present ---
171
- if [[ -n "$TOOL_NAME" ]] && [[ "$TOOL_NAME" != "null" ]]; then
172
- # Check if we should skip this routine tool
173
- if should_skip_tool "$TOOL_NAME" "$TOOL_INPUT"; then
174
- # Still increment counter but don't store
175
- if [[ -f "$COUNTER_FILE" ]]; then
176
- CURRENT_COUNT=$(cat "$COUNTER_FILE" 2>/dev/null || echo "0")
177
- else
178
- CURRENT_COUNT=0
179
- fi
180
- echo "$((CURRENT_COUNT + 1))" > "$COUNTER_FILE"
181
- log "Counter incremented to $((CURRENT_COUNT + 1)) (routine tool skipped)"
213
+ # Skip storing low-significance tools
214
+ if [[ "$SIGNIFICANCE" -lt "$MIN_SIGNIFICANCE" ]]; then
215
+ log "Tool below significance threshold, skipping storage (count: $NEW_COUNT)"
182
216
  else
183
217
  log "Storing significant tool use: $TOOL_NAME"
184
218
 
185
219
  # Truncate long outputs
186
- if [[ ${#TOOL_OUTPUT} -gt 1000 ]]; then
187
- TOOL_OUTPUT="${TOOL_OUTPUT:0:1000}... [truncated]"
220
+ if [[ ${#TOOL_OUTPUT} -gt 800 ]]; then
221
+ TOOL_OUTPUT="${TOOL_OUTPUT:0:800}... [truncated]"
188
222
  fi
189
223
 
190
224
  # Get previous tool ID for causal chaining
@@ -193,54 +227,44 @@ if [[ -n "$TOOL_NAME" ]] && [[ "$TOOL_NAME" != "null" ]]; then
193
227
  PREV_TOOL_ID=$(cat "$LAST_TOOL_ID_FILE" 2>/dev/null)
194
228
  fi
195
229
 
196
- # Determine significance level
197
- SIGNIFICANCE="normal"
198
- if is_significant_output "$TOOL_OUTPUT"; then
199
- SIGNIFICANCE="high"
200
- fi
201
-
202
- # Detect domain for entity extraction
230
+ # Determine domain
203
231
  DOMAIN="code"
204
- if [[ "$TOOL_NAME" == "WebFetch" ]] || [[ "$TOOL_NAME" == "WebSearch" ]]; then
205
- DOMAIN="web"
206
- elif [[ "$TOOL_NAME" == "TodoWrite" ]]; then
207
- DOMAIN="planning"
208
- fi
232
+ case "$TOOL_NAME" in
233
+ WebFetch|WebSearch) DOMAIN="web" ;;
234
+ TodoWrite) DOMAIN="planning" ;;
235
+ esac
209
236
 
210
- # Build tool use content
211
- TOOL_CONTENT="[Tool Use] $TOOL_NAME
237
+ # Build compact tool content
238
+ TOOL_CONTENT="[Tool] $TOOL_NAME (sig: $SIGNIFICANCE)
212
239
  Project: $PROJECT_NAME
213
- Significance: $SIGNIFICANCE
214
- Input: ${TOOL_INPUT:0:500}
240
+ Input: ${TOOL_INPUT:0:400}
215
241
  Output: $TOOL_OUTPUT"
216
242
 
217
- # Build enhanced payload for tool use with entity extraction
243
+ # Build payload
218
244
  TOOL_PAYLOAD=$(jq -n \
219
245
  --arg content "$TOOL_CONTENT" \
220
246
  --arg tool "$TOOL_NAME" \
221
247
  --arg project "$PROJECT_NAME" \
222
- --arg projectDir "$PROJECT_DIR" \
223
248
  --arg timestamp "$TIMESTAMP" \
224
249
  --arg interactionId "$INTERACTION_ID" \
225
250
  --arg prevToolId "$PREV_TOOL_ID" \
226
- --arg significance "$SIGNIFICANCE" \
251
+ --argjson significance "$SIGNIFICANCE" \
227
252
  --arg domain "$DOMAIN" \
228
253
  --argjson extractEntities "$EXTRACT_ENTITIES" \
229
254
  --argjson createRelations "$CREATE_RELATIONS" \
230
255
  '{
231
256
  content: $content,
232
- tags: ["claude-code", "type:tool-use", "tool:\($tool)", "project:\($project)", "significance:\($significance)"],
257
+ tags: ["claude-code", "type:tool-use", "tool:\($tool)", "project:\($project)", "sig:\($significance)"],
233
258
  metadata: {
234
259
  eventType: "tool-use",
235
260
  toolName: $tool,
236
261
  projectName: $project,
237
- projectDir: $projectDir,
238
262
  timestamp: $timestamp,
239
263
  interactionId: $interactionId,
240
264
  significance: $significance
241
265
  },
242
266
  extract_entities: $extractEntities,
243
- entity_types: ["code_pattern", "error", "file", "function", "class", "command"],
267
+ entity_types: ["code_pattern", "error", "file", "function", "command"],
244
268
  domain: $domain,
245
269
  create_relationships: $createRelations,
246
270
  episodic: {
@@ -250,49 +274,45 @@ Output: $TOOL_OUTPUT"
250
274
  }
251
275
  }')
252
276
 
253
- # Store tool use asynchronously with enhanced GraphRAG features
277
+ # Store tool use asynchronously
254
278
  (
255
- RESPONSE=$(curl -s -w "\n%{http_code}" -X POST "$NEXUS_API_URL/api/memory/store" \
279
+ RESPONSE=$(curl -s -X POST "$NEXUS_API_URL/api/memory/store" \
256
280
  -H "Content-Type: application/json" \
257
281
  -H "Authorization: Bearer $NEXUS_API_KEY" \
258
282
  -H "X-Company-ID: $COMPANY_ID" \
259
283
  -H "X-App-ID: $APP_ID" \
260
284
  -H "X-User-ID: ${USER:-unknown}" \
285
+ -H "Connection: keep-alive" \
261
286
  -d "$TOOL_PAYLOAD" \
287
+ --connect-timeout 2 \
262
288
  --max-time 5 2>/dev/null)
263
289
 
264
- HTTP_CODE=$(echo "$RESPONSE" | tail -n1)
265
- BODY=$(echo "$RESPONSE" | sed '$d')
266
-
267
290
  # Save this tool's memory ID for causal chaining
268
- MEMORY_ID=$(echo "$BODY" | jq -r '.data.memoryId // .memoryId // empty' 2>/dev/null)
291
+ MEMORY_ID=$(echo "$RESPONSE" | jq -r '.data.memoryId // .memoryId // empty' 2>/dev/null)
269
292
  if [[ -n "$MEMORY_ID" ]]; then
270
293
  echo "$MEMORY_ID" > "$LAST_TOOL_ID_FILE"
271
294
  fi
272
295
 
273
- # Append to session tools log for episode summary
274
- echo "$TOOL_NAME:$SIGNIFICANCE:$INTERACTION_ID" >> "$SESSION_TOOLS_FILE"
275
-
296
+ # Append to session tools log (compact format)
297
+ echo "$TOOL_NAME:$SIGNIFICANCE:$(date +%H%M%S)" >> "$SESSION_TOOLS_FILE"
276
298
  ) &
277
299
 
278
- log "Tool use stored with causal chain"
300
+ log "Tool use stored"
279
301
  fi
280
- fi
281
-
282
- # --- STEP 2: Check if we should generate episode summary ---
283
-
284
- # Get current counter
285
- if [[ -f "$COUNTER_FILE" ]]; then
286
- CURRENT_COUNT=$(cat "$COUNTER_FILE" 2>/dev/null || echo "0")
287
302
  else
288
- CURRENT_COUNT=0
303
+ # No tool - just get counter
304
+ if [[ -f "$COUNTER_FILE" ]]; then
305
+ CURRENT_COUNT=$(cat "$COUNTER_FILE" 2>/dev/null || echo "0")
306
+ else
307
+ CURRENT_COUNT=0
308
+ fi
309
+ NEW_COUNT=$((CURRENT_COUNT + 1))
310
+ echo "$NEW_COUNT" > "$COUNTER_FILE"
289
311
  fi
290
312
 
291
- # Increment counter
292
- NEW_COUNT=$((CURRENT_COUNT + 1))
293
- echo "$NEW_COUNT" > "$COUNTER_FILE"
294
-
295
- log "Tool count: $NEW_COUNT (threshold: $EPISODE_THRESHOLD)"
313
+ # =========================================================
314
+ # EPISODE SUMMARY GENERATION
315
+ # =========================================================
296
316
 
297
317
  # Determine if we should generate an episode summary
298
318
  SHOULD_SUMMARIZE=false
@@ -305,8 +325,7 @@ elif [[ "$SESSION_END" == "true" ]]; then
305
325
  log "Session end summary"
306
326
  elif [[ "$NEW_COUNT" -ge "$EPISODE_THRESHOLD" ]]; then
307
327
  SHOULD_SUMMARIZE=true
308
- log "Threshold reached, generating episode"
309
- # Reset counter
328
+ log "Threshold reached ($NEW_COUNT >= $EPISODE_THRESHOLD)"
310
329
  echo "0" > "$COUNTER_FILE"
311
330
  fi
312
331
 
@@ -316,74 +335,74 @@ if [[ "$SHOULD_SUMMARIZE" != "true" ]]; then
316
335
  exit 0
317
336
  fi
318
337
 
319
- # Generate session ID for this episode
338
+ # =========================================================
339
+ # BUILD EPISODE SUMMARY
340
+ # =========================================================
341
+
320
342
  SESSION_ID=$(date +%Y%m%d-%H%M%S)
321
343
 
322
344
  # Read session tools for summary
323
345
  SESSION_TOOLS=""
346
+ SIGNIFICANT_TOOLS=""
324
347
  if [[ -f "$SESSION_TOOLS_FILE" ]]; then
325
- SESSION_TOOLS=$(cat "$SESSION_TOOLS_FILE" 2>/dev/null | tail -20)
326
- # Clear the session tools file after reading
348
+ SESSION_TOOLS=$(cat "$SESSION_TOOLS_FILE" 2>/dev/null | tail -30)
349
+ # Count high-significance tools (sig >= 60)
350
+ SIGNIFICANT_TOOLS=$(echo "$SESSION_TOOLS" | awk -F: '$2 >= 60 {print $1}' | sort | uniq -c | sort -rn | head -5)
351
+ # Clear the session tools file
327
352
  echo "" > "$SESSION_TOOLS_FILE"
328
353
  fi
329
354
 
330
- # Skip if no content to summarize
355
+ # Build content from session activity if not provided
331
356
  if [[ -z "$CONTENT" ]] || [[ "$CONTENT" == "null" ]]; then
332
- # Build content from session tools
333
357
  if [[ -n "$SESSION_TOOLS" ]]; then
334
- CONTENT="Session activity:
335
- $SESSION_TOOLS"
358
+ CONTENT="Session activity in $PROJECT_NAME"
336
359
  else
337
360
  CONTENT="Conversation segment in $PROJECT_NAME project"
338
361
  fi
339
362
  fi
340
363
 
341
364
  # Truncate very long content
342
- if [[ ${#CONTENT} -gt 5000 ]]; then
343
- CONTENT="${CONTENT:0:5000}... [truncated]"
365
+ if [[ ${#CONTENT} -gt 3000 ]]; then
366
+ CONTENT="${CONTENT:0:3000}... [truncated]"
344
367
  fi
345
368
 
346
- # Count significant tools in this episode
347
- SIGNIFICANT_COUNT=$(echo "$SESSION_TOOLS" | grep -c ":high:" || echo "0")
369
+ # Count tools by significance
370
+ TOTAL_TOOLS=$(echo "$SESSION_TOOLS" | grep -c ':' || echo "0")
371
+ HIGH_SIG_COUNT=$(echo "$SESSION_TOOLS" | awk -F: '$2 >= 60' | wc -l | tr -d ' ')
348
372
 
349
- # Generate episode summary with enhanced structure
373
+ # Generate compact episode summary
350
374
  EPISODE_CONTENT=$(cat <<EOF
351
- [Episode Summary - $PROJECT_NAME]
352
- Timestamp: $TIMESTAMP
353
- Project: $PROJECT_NAME
354
- Directory: $PROJECT_DIR
355
- Tool Count: $NEW_COUNT
356
- Significant Events: $SIGNIFICANT_COUNT
375
+ [Episode] $PROJECT_NAME
376
+ Time: $TIMESTAMP
377
+ Tools: $TOTAL_TOOLS total, $HIGH_SIG_COUNT significant
357
378
 
358
- Session Activity:
379
+ Activity Summary:
359
380
  $CONTENT
360
381
 
361
- Tools Used:
362
- $(echo "$SESSION_TOOLS" | awk -F: '{print "- " $1 " (" $2 ")"}' | head -10)
382
+ Top Tools:
383
+ $(echo "$SIGNIFICANT_TOOLS" | head -5 | awk '{print "- " $2 " (" $1 "x)"}')
363
384
 
364
385
  ---
365
- This episode was automatically generated after $NEW_COUNT tool interactions.
386
+ Auto-generated after $NEW_COUNT interactions.
366
387
  EOF
367
388
  )
368
389
 
369
- log "Generating episode summary for $PROJECT_NAME"
390
+ log "Generating episode summary"
370
391
 
371
392
  # Get previous episode ID for causal chaining
372
393
  PREV_EPISODE_ID=""
373
- LAST_EPISODE_FILE="${STATE_DIR}/last_episode_id"
374
394
  if [[ -f "$LAST_EPISODE_FILE" ]]; then
375
395
  PREV_EPISODE_ID=$(cat "$LAST_EPISODE_FILE" 2>/dev/null)
376
396
  fi
377
397
 
378
- # Build the enhanced payload with causal chaining
398
+ # Build the payload
379
399
  PAYLOAD=$(jq -n \
380
400
  --arg content "$EPISODE_CONTENT" \
381
401
  --arg session "$SESSION_ID" \
382
402
  --arg project "$PROJECT_NAME" \
383
- --arg projectDir "$PROJECT_DIR" \
384
403
  --arg timestamp "$TIMESTAMP" \
385
404
  --argjson toolCount "$NEW_COUNT" \
386
- --argjson significantCount "$SIGNIFICANT_COUNT" \
405
+ --argjson sigCount "$HIGH_SIG_COUNT" \
387
406
  --arg prevEpisodeId "$PREV_EPISODE_ID" \
388
407
  --argjson extractEntities "$EXTRACT_ENTITIES" \
389
408
  --argjson createRelations "$CREATE_RELATIONS" \
@@ -393,11 +412,10 @@ PAYLOAD=$(jq -n \
393
412
  metadata: {
394
413
  sessionId: $session,
395
414
  eventType: "episode",
396
- projectDir: $projectDir,
397
415
  projectName: $project,
398
416
  timestamp: $timestamp,
399
417
  toolCount: $toolCount,
400
- significantEventCount: $significantCount,
418
+ significantToolCount: $sigCount,
401
419
  isEpisodeSummary: true
402
420
  },
403
421
  extract_entities: $extractEntities,
@@ -415,37 +433,36 @@ PAYLOAD=$(jq -n \
415
433
 
416
434
  log "Storing episode to $NEXUS_API_URL/api/memory/store"
417
435
 
418
- # Store to GraphRAG via API Gateway
436
+ # Store episode
419
437
  if [[ "$VERBOSE" == "1" ]]; then
420
- # Sync mode with output for debugging
421
438
  RESPONSE=$(curl -s -w "\n%{http_code}" -X POST "$NEXUS_API_URL/api/memory/store" \
422
439
  -H "Content-Type: application/json" \
423
440
  -H "Authorization: Bearer $NEXUS_API_KEY" \
424
441
  -H "X-Company-ID: $COMPANY_ID" \
425
442
  -H "X-App-ID: $APP_ID" \
426
443
  -H "X-User-ID: ${USER:-unknown}" \
444
+ -H "Connection: keep-alive" \
427
445
  -d "$PAYLOAD" \
428
- --max-time 10 2>&1)
446
+ --connect-timeout 2 \
447
+ --max-time 8 2>&1)
429
448
 
430
449
  HTTP_CODE=$(echo "$RESPONSE" | tail -n1)
431
450
  BODY=$(echo "$RESPONSE" | sed '$d')
432
451
 
433
452
  log "Response code: $HTTP_CODE"
434
- log "Response body: ${BODY:0:500}"
435
453
 
436
454
  if [[ "$HTTP_CODE" == "200" ]] || [[ "$HTTP_CODE" == "201" ]]; then
437
- # Save episode ID for causal chaining
438
455
  EPISODE_ID=$(echo "$BODY" | jq -r '.data.memoryId // .memoryId // empty' 2>/dev/null)
439
456
  if [[ -n "$EPISODE_ID" ]]; then
440
457
  echo "$EPISODE_ID" > "$LAST_EPISODE_FILE"
441
- log "Saved episode ID for causal chain: $EPISODE_ID"
458
+ log "Saved episode ID: $EPISODE_ID"
442
459
  fi
443
460
  log "Episode stored successfully"
444
461
  else
445
462
  log_error "Failed to store episode (HTTP $HTTP_CODE)"
446
463
  fi
447
464
  else
448
- # Async mode for normal operation
465
+ # Async mode
449
466
  (
450
467
  RESPONSE=$(curl -s -X POST "$NEXUS_API_URL/api/memory/store" \
451
468
  -H "Content-Type: application/json" \
@@ -453,10 +470,11 @@ else
453
470
  -H "X-Company-ID: $COMPANY_ID" \
454
471
  -H "X-App-ID: $APP_ID" \
455
472
  -H "X-User-ID: ${USER:-unknown}" \
473
+ -H "Connection: keep-alive" \
456
474
  -d "$PAYLOAD" \
475
+ --connect-timeout 2 \
457
476
  --max-time 5 2>/dev/null)
458
477
 
459
- # Save episode ID for causal chaining
460
478
  EPISODE_ID=$(echo "$RESPONSE" | jq -r '.data.memoryId // .memoryId // empty' 2>/dev/null)
461
479
  if [[ -n "$EPISODE_ID" ]]; then
462
480
  echo "$EPISODE_ID" > "$LAST_EPISODE_FILE"