fix: feat: SessionStart compact hook re-injects phase protocol after context compaction (#274)

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
openhands 2026-03-20 23:27:32 +00:00
parent 5c960f1b6e
commit e3895ad3ac
7 changed files with 86 additions and 8 deletions

View file

@ -218,6 +218,39 @@ create_agent_session() {
fi
rm -f "$exit_marker"
# Install SessionStart hook for context re-injection after compaction:
# when Claude Code compacts context during long sessions, the phase protocol
# instructions are lost. This hook fires after each compaction and outputs
# the content of a context file so Claude retains critical instructions.
# The context file is written by callers via write_compact_context().
if [ -n "$phase_file" ]; then
local compact_hook_script="${FACTORY_ROOT}/lib/hooks/on-compact-reinject.sh"
if [ -x "$compact_hook_script" ]; then
local context_file="${phase_file%.phase}.context"
local compact_hook_cmd="${compact_hook_script} ${context_file}"
if [ -f "$settings" ]; then
jq --arg cmd "$compact_hook_cmd" '
if (.hooks.SessionStart // [] | any(.[]; .hooks[]?.command == $cmd))
then .
else .hooks.SessionStart = (.hooks.SessionStart // []) + [{
matcher: "compact",
hooks: [{type: "command", command: $cmd}]
}]
end
' "$settings" > "${settings}.tmp" && mv "${settings}.tmp" "$settings"
else
jq -n --arg cmd "$compact_hook_cmd" '{
hooks: {
SessionStart: [{
matcher: "compact",
hooks: [{type: "command", command: $cmd}]
}]
}
}' > "$settings"
fi
fi
fi
# Install Stop hook for Matrix streaming: when MATRIX_THREAD_ID is set,
# each Claude response is posted to the Matrix thread so humans can follow.
local matrix_hook_script="${FACTORY_ROOT}/lib/hooks/on-stop-matrix.sh"
@ -392,6 +425,16 @@ monitor_phase_loop() {
done
}
# Write context to a file for re-injection after context compaction.
# The SessionStart compact hook reads this file and outputs it to stdout.
# Args: phase_file content
write_compact_context() {
local phase_file="$1"
local content="$2"
local context_file="${phase_file%.phase}.context"
printf '%s\n' "$content" > "$context_file"
}
# Kill a tmux session gracefully (no-op if not found).
agent_kill_session() {
local session="${1:-}"

View file

@ -202,6 +202,11 @@ run_formula_and_monitor() {
exit 1
fi
# Write phase protocol to context file for compaction survival
if [ -n "${PROMPT_FOOTER:-}" ]; then
write_compact_context "$PHASE_FILE" "$PROMPT_FOOTER"
fi
agent_inject_into_session "$SESSION_NAME" "$PROMPT"
log "Prompt sent to tmux session"
matrix_send "$agent_name" "${agent_name^} session started for ${CODEBERG_REPO}" 2>/dev/null || true

View file

@ -0,0 +1,15 @@
#!/bin/bash
# on-compact-reinject.sh — SessionStart (compact) hook for dark-factory agent sessions.
#
# Called by Claude Code after context compaction. Reads a context file and
# outputs its content to stdout, which Claude Code injects as system context.
# No-op if the context file doesn't exist.
#
# Usage (in .claude/settings.json):
# {"type": "command", "command": "this-script /tmp/dev-session-PROJECT-ISSUE.context"}
#
# Args: $1 = context file path
cat > /dev/null # consume hook JSON from stdin
[ -n "${1:-}" ] && [ -f "$1" ] && cat "$1"
exit 0