Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
36 changes: 25 additions & 11 deletions plugin/codex/scripts/post-compaction.sh
Original file line number Diff line number Diff line change
Expand Up @@ -31,8 +31,24 @@ fi
ENCODED_PROJECT=$(printf '%s' "$PROJECT" | jq -sRr @uri)
CONTEXT=$(curl -sf "${ENGRAM_URL}/context?project=${ENCODED_PROJECT}" --max-time 3 2>/dev/null | jq -r '.context // empty')

# Inject Memory Protocol + compaction instruction + context
cat <<'PROTOCOL'
# Build Memory Protocol + compaction instruction + context, then emit it as
# Codex's hookSpecificOutput JSON envelope. Codex's SessionStart parser (this
# hook registers under the SessionStart "compact" matcher — see hooks.json)
# rejects raw stdout text as a hook failure (non-fatal, but reports
# "hook: SessionStart Failed" every run — see codex-review SKILL.md "Failure
# modes", 2026-08-08 silent-death-after-hooks). Claude Code's SessionStart
# contract stays tolerant of raw text, so this is a codex/-only change;
# plugin/claude-code/scripts/post-compaction.sh is untouched.
#
# Each piece is written to a FILE, never captured via `$(cat <<'EOF' ...)` —
# macOS ships bash 3.2 as /bin/bash, which mis-parses a heredoc nested inside
# a command substitution once the heredoc body contains an apostrophe (e.g.
# "user's"), throwing "unexpected EOF while looking for matching `)'" at
# script-load time. Writing to files sidesteps the parser bug entirely.
TMPD=$(mktemp -d)
trap 'rm -rf "$TMPD"' EXIT

cat <<'PROTOCOL' > "$TMPD/head.txt"
Comment on lines +48 to +51

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🩺 Stability & Availability | 🟡 Minor | ⚡ Quick win

Stop when temporary-directory creation fails.

If mktemp -d fails, TMPD is empty. The next redirect writes to /head.txt. The cleanup trap does not remove those files.

Proposed fix
-TMPD=$(mktemp -d)
+TMPD=$(mktemp -d) || {
+  printf '%s\n' "Could not create temporary directory" >&2
+  exit 1
+}
 trap 'rm -rf "$TMPD"' EXIT
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
TMPD=$(mktemp -d)
trap 'rm -rf "$TMPD"' EXIT
cat <<'PROTOCOL' > "$TMPD/head.txt"
TMPD=$(mktemp -d) || {
printf '%s\n' "Could not create temporary directory" >&2
exit 1
}
trap 'rm -rf "$TMPD"' EXIT
cat <<'PROTOCOL' > "$TMPD/head.txt"
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@plugin/codex/scripts/post-compaction.sh` around lines 48 - 51, Validate the
result of mktemp -d immediately after assigning TMPD and exit with an error if
directory creation fails, before installing the cleanup trap or writing
TMPD/head.txt. Keep the existing temporary-directory cleanup behavior for
successful creation.

## Engram Persistent Memory — ACTIVE PROTOCOL

You have engram memory tools. This protocol is MANDATORY and ALWAYS ACTIVE.
Expand Down Expand Up @@ -68,21 +84,19 @@ Call `mem_session_summary` with: Goal, Discoveries, Accomplished, Next Steps, Re
CRITICAL INSTRUCTION POST-COMPACTION — follow these steps IN ORDER:
PROTOCOL

printf "\n1. FIRST: Call mem_session_summary with the content of the compacted summary above. Use project: '%s'.\n" "$PROJECT"
printf " This preserves what was accomplished before compaction.\n\n"
printf "2. THEN: Call mem_context with project: '%s' to recover recent session history and observations.\n" "$PROJECT"
printf " Read the returned context carefully — it tells you what was being worked on.\n\n"
cat <<'PROTOCOL'
printf "1. FIRST: Call mem_session_summary with the content of the compacted summary above. Use project: '%s'.\n This preserves what was accomplished before compaction.\n\n2. THEN: Call mem_context with project: '%s' to recover recent session history and observations.\n Read the returned context carefully — it tells you what was being worked on." \
"$PROJECT" "$PROJECT" > "$TMPD/steps.txt"

cat <<'PROTOCOL' > "$TMPD/tail.txt"
3. If you need more detail on a specific topic, call mem_search with relevant keywords.

4. Only THEN continue working on what the user asked.

All 4 steps are MANDATORY. Without them, you lose context and start blind.
PROTOCOL

# Inject memory context if available
if [ -n "$CONTEXT" ]; then
printf "\n%s\n" "$CONTEXT"
fi
jq -n --rawfile head "$TMPD/head.txt" --rawfile steps "$TMPD/steps.txt" --rawfile tail "$TMPD/tail.txt" --arg ctx "$CONTEXT" \
'{hookSpecificOutput: {hookEventName: "SessionStart",
additionalContext: ($head + "\n\n" + $steps + "\n\n" + $tail + (if $ctx != "" then "\n\n" + $ctx else "" end))}}'

exit 0
24 changes: 18 additions & 6 deletions plugin/codex/scripts/session-start.sh
Original file line number Diff line number Diff line change
Expand Up @@ -137,8 +137,21 @@ fi
ENCODED_PROJECT=$(printf '%s' "$PROJECT" | jq -sRr @uri)
CONTEXT=$(curl -sf "${ENGRAM_URL}/context?project=${ENCODED_PROJECT}" --max-time 3 2>/dev/null | jq -r '.context // empty')

# Inject Memory Protocol + context — stdout is returned to Codex as additionalContext
cat <<'PROTOCOL'
# Build Memory Protocol + context, then emit it as Codex's hookSpecificOutput
# JSON envelope. Codex's SessionStart parser rejects raw stdout text as a hook
# failure (non-fatal, but reports "hook: SessionStart Failed" every run — see
# codex-review SKILL.md "Failure modes", 2026-08-08 silent-death-after-hooks).
# Claude Code's SessionStart contract stays tolerant of raw text, so this is a
# codex/-only change; plugin/claude-code/scripts/session-start.sh is untouched.
#
# The protocol text is written to a FILE, never captured via `$(cat <<'EOF' ...)`
# — macOS ships bash 3.2 as /bin/bash, which mis-parses a heredoc nested inside
# a command substitution once the heredoc body contains an apostrophe (e.g.
# "user's"), throwing "unexpected EOF while looking for matching `)'" at
# script-load time. Writing to a file sidesteps the parser bug entirely.
PROTOCOL_FILE=$(mktemp)
trap 'rm -f "$PROTOCOL_FILE"' EXIT
cat <<'PROTOCOL' > "$PROTOCOL_FILE"
Comment on lines +140 to +154

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

📐 Maintainability & Code Quality | 🟠 Major | 🏗️ Heavy lift

Move protocol construction out of the plugin adapters.

These scripts build Memory Protocol policy and require jq at adapter runtime. Move protocol and hook-envelope generation into the core Go API or tool. Keep each adapter limited to input parsing, core invocation, and returning the result.

  • plugin/codex/scripts/session-start.sh#L140-L154: Replace shell-based protocol construction with a core API or tool call.
  • plugin/codex/scripts/session-start.sh#L190-L192: Return the structured result from the core API or tool.
  • plugin/codex/scripts/post-compaction.sh#L34-L51: Move protocol and recovery-content construction into the core API or tool.
  • plugin/codex/scripts/post-compaction.sh#L87-L100: Return the structured result from the core API or tool.

As per path instructions, plugin/**: Adapters stay thin: parse input, call the core Go API/tool, return. No business logic, no external runtime deps.

📍 Affects 2 files
  • plugin/codex/scripts/session-start.sh#L140-L154 (this comment)
  • plugin/codex/scripts/session-start.sh#L190-L192
  • plugin/codex/scripts/post-compaction.sh#L34-L51
  • plugin/codex/scripts/post-compaction.sh#L87-L100
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@plugin/codex/scripts/session-start.sh` around lines 140 - 154, Move Memory
Protocol, recovery-content, and hook-envelope generation out of the adapter
scripts into the core Go API or tool, removing runtime jq and shell-based
business logic. In plugin/codex/scripts/session-start.sh at lines 140-154,
replace protocol construction with the core invocation and at lines 190-192
return its structured result; apply the equivalent changes in
plugin/codex/scripts/post-compaction.sh at lines 34-51 and 87-100. Keep both
adapters limited to input parsing, core invocation, and returning the structured
result.

Source: Path instructions

## Engram Persistent Memory — ACTIVE PROTOCOL

You have engram memory tools. This protocol is MANDATORY and ALWAYS ACTIVE.
Expand Down Expand Up @@ -174,9 +187,8 @@ Call `mem_save` IMMEDIATELY after ANY of these:
Call `mem_session_summary` with: Goal, Discoveries, Accomplished, Next Steps, Relevant Files.
PROTOCOL

# Inject memory context if available
if [ -n "$CONTEXT" ]; then
printf "\n%s\n" "$CONTEXT"
fi
jq -n --rawfile protocol "$PROTOCOL_FILE" --arg ctx "$CONTEXT" \
'{hookSpecificOutput: {hookEventName: "SessionStart",
additionalContext: ($protocol + (if $ctx != "" then "\n\n" + $ctx else "" end))}}'

exit 0