Files
roafacturare/docs/sessionstart_ref.md
2026-09-17 22:36:46 +03:00

21 KiB

# Referinta oficiala: SessionStart / PostCompact / Stop (surse: code.claude.com/docs)

Toate citatele de mai jos sunt verbatim din versiunile .md brute ale paginilor (https://code.claude.com/docs/en/hooks.md, .../hooks-guide.md), fetch-uite direct cu curl pe 2026-09-17 (WebFetch trunchiaza paginile astea, sunt prea mari pentru modelul intern al tool-ului — vezi nota metodologica de la final). Nu am dedus nimic; unde n-am gasit un raspuns explicit, scriu "NEDOCUMENTAT".


1. SessionStart — valori de matcher acceptate

CONFIRMAT, dar lista din brief e incompleta: sunt 5 valori, nu 4. Lipsea fork.

Sursa: https://code.claude.com/docs/en/hooks.md, sectiunea ### SessionStart:

The matcher value corresponds to how the session was initiated:

Matcher When it fires
startup New session
resume --resume, --continue, or /resume
clear /clear
compact Auto or manual compaction
fork A new session forked from an existing one: --fork-session with --resume or --continue, the /fork background copy, or /branch

Before v2.1.214, forked sessions reported source "resume".

Important pentru mecanismul propus: compact e si el un matcher de SessionStart, separat de evenimentul PostCompact (vezi punctul 4). Adica dupa o compactare (auto sau manuala), Claude Code re-porneste efectiv un SessionStart cu source: "compact", iar acela SUPORTA additionalContext — spre deosebire de PostCompact propriu-zis, care nu suporta (punctul 4).


2. SessionStart — campuri pe stdin, cum distinge /clear de pornire normala

Sursa: https://code.claude.com/docs/en/hooks.md, #### SessionStart input:

In addition to the common input fields, SessionStart hooks receive source and optionally model, agent_type, and session_title:

Field Description
source How the session started: "startup" for new sessions, "resume" for resumed sessions, "clear" after /clear, "compact" after compaction, or "fork" for a new session forked from an existing one
model The active model identifier. It can be omitted, for example after /clear or when a session is restored through conversation recovery, so check for the field before reading it
agent_type The agent name, present when you start Claude Code with claude --agent <name>
session_title The current session title if one is already set, for example via --name or /rename. A hook that emits sessionTitle can check session_title first to avoid overwriting a title the user set explicitly

Distinctia /clear vs pornire normala se face STRICT prin campul source: "clear" vs "startup". Nu exista alt semnal necesar.

Campuri comune (din #### Common input fields, aceeasi pagina), relevante pentru mecanism: session_id, prompt_id, transcript_path, cwd, scratchpad_dir, permission_mode, effort, hook_event_name. Citat exact pentru transcript_path:

transcript_path — Path to conversation JSON. The transcript file is written asynchronously and may lag the in-memory conversation, so it may not yet include the current turn's most recent messages when a hook fires.

Cand source e "resume" sau "fork" si transcript-ul are cel putin un raspuns Claude, mai vin 4 campuri (necesare v2.1.251+): seconds_since_last_response, context_tokens, prompt_cache_likely_expired, estimated_cache_write_usd. Aceste campuri NU apar pentru source: "clear" — deci hook-ul nu primeste de la Claude Code o estimare gata facuta a cate token-i "costa" reluarea; asta ramane treaba handoff-ului scris pe disc.

agent_type si agent_id (subagent-only) — prezente doar cand hook-ul ruleaza intr-un subagent sau sesiunea foloseste --agent.

Timing: la pornire/--resume/--continue//clear, hook-urile SessionStart ruleaza in fundal — poti scrie imediat, dar primul raspuns al lui Claude asteapta sa termine hook-urile. La /resume in interiorul unei sesiuni, switch-ul asteapta hook-urile. Citat:

When you start an interactive session, resume a conversation at launch with --continue or --resume, or run /clear, SessionStart hooks run in the background. You can type right away, and a conversation you resumed appears without waiting for the hooks. Claude's first response still waits for the hooks to finish, so their context reaches Claude.


3. SessionStart — cum returneaza context, forma exacta, limita de marime

Sursa: https://code.claude.com/docs/en/hooks.md, #### SessionStart decision control + exemplu JSON:

In addition to the JSON output fields available to all hooks, you can return these event-specific fields:

Field Description
additionalContext String added to Claude's context at the start of the conversation, before the first prompt.
initialUserMessage String used as the first user message of the session. Applies in non-interactive mode with -p...
sessionTitle Sets the session title, with the same effect as /rename... Applies when source is "startup", "resume", or "fork"; ignored on "clear" and "compact"
watchPaths Array of absolute paths to watch for FileChanged events during this session
reloadSkills Boolean. When true, re-scans skill/command dirs after SessionStart hooks complete...
{
  "hookSpecificOutput": {
    "hookEventName": "SessionStart",
    "additionalContext": "Current branch: feat/auth-refactor\nUncommitted changes: src/auth.ts, src/login.tsx\nActive issue: #4211 Migrate to OAuth2",
    "sessionTitle": "auth-refactor"
  }
}

Confirmat: e hookSpecificOutput cu hookEventName: "SessionStart" si additionalContext imbricat inauntru — exact forma presupusa in brief.

Limita de marime (generala, se aplica la SessionStart la fel ca la toate evenimentele), sursa: https://code.claude.com/docs/en/hooks.md, #### Add context for Claude:

The additionalContext field passes a string from your hook into Claude's context window. Claude Code wraps the string in a system reminder and inserts it into the conversation at the point where the hook fired.

[...]

If a value exceeds 10,000 characters, Claude Code writes the text to a file in the session directory and passes Claude the file path with a short preview instead.

Deci: 10.000 de caractere e pragul. Peste el, Claude Code NU trunchiaza si NU refuza — scrie continutul intr-un fisier in directorul sesiunii si trimite lui Claude calea + un preview scurt. Pentru un handoff mare, asta e de fapt convenabil: hook-ul poate trimite tot textul, iar peste prag Claude Code il redirectioneaza singur spre fisier.

Unde ajunge textul pentru SessionStart specific, din acelasi paragraf:

Nota despre re-rulare la resume, aceeasi sectiune:

SessionStart hooks run again on resume with source set to "resume", or "fork" if you added --fork-session, so they can refresh their context.

(Nu mentioneaza explicit re-rularea la /clear, dar tabelul de matchere de la punctul 1 o confirma separat: clear e chiar unul dintre matcherele native ale evenimentului.)

Exit code: stdout simplu (fara JSON) e tratat ca text simplu si ajunge in context, la fel ca additionalContext — nu trebuie neaparat JSON daca hook-ul nu seteaza si alte campuri. Citat:

Claude Code adds stdout it treats as plain text to Claude's context. [...] Since plain stdout already reaches Claude for this event, a hook that only loads context can print to stdout directly without building JSON. Use the JSON form when you need to combine context with other fields such as sessionTitle.


4. PostCompact — aceleasi intrebari, plus manual vs auto

Sursa: https://code.claude.com/docs/en/hooks.md, ### PostCompact:

Runs after Claude Code completes a compact operation. Use this event to react to the new compacted state, for example to log the generated summary or update external state. Claude Code discards a PostCompact hook's systemMessage and continue fields.

The same matcher values apply as for PreCompact:

Matcher When it fires
manual After /compact
auto After auto-compact when the conversation reaches the auto-compact window

PostCompact input

In addition to the common input fields, PostCompact hooks receive trigger and compact_summary. The compact_summary field contains the conversation summary generated by the compact operation.

{
  "session_id": "abc123",
  "transcript_path": "...",
  "cwd": "...",
  "hook_event_name": "PostCompact",
  "trigger": "manual",
  "compact_summary": "Summary of the compacted conversation..."
}

PostCompact hooks have no decision control. They can't affect the compaction result but can perform follow-up tasks.

Constatare critica pentru mecanismul propus: PostCompact NU poate injecta additionalContext si nu poate influenta rezultatul compactarii — e strict pentru efecte secundare (log, notificare externa etc.). Daca planul se baza pe "PostCompact reinjecteaza handoff-ul", premisa e falsa. Calea care CHIAR functioneaza pentru compactare e SessionStart cu matcher compact (punctul 1 si 3) — un eveniment diferit, care ruleaza dupa PostCompact si care are additionalContext. Exemplul oficial de "re-inject context after compaction" din hooks-guide.md confirma asta explicit:

When Claude's context window fills up, compaction summarizes the conversation to free space. This can lose important details. Use a SessionStart hook with a compact matcher to re-inject critical context after every compaction.

Pentru PreCompact (nu a fost cerut explicit, dar e relevant ca sa nu confundati): poate bloca (decision: "block" sau exit 2) si primeste trigger + custom_instructions, dar la fel "discards systemMessage and continue". PreCompact nu e mecanismul de reinjectare — ruleaza INAINTE de compactare.


5. Poate un hook sa declanseze /clear sau /compact?

NU exista niciun mecanism. Confirmat explicit, nu doar prin absenta.

Sursa: https://code.claude.com/docs/en/hooks-guide.md, linia 952:

Command hooks communicate through stdout, stderr, and exit codes only. They can't trigger / commands or tool calls. Text returned via additionalContext is injected as a system reminder that Claude reads as plain text. HTTP hooks communicate through the response body instead.

Am cautat explicit orice camp de tip "command" sau mecanism de rulare a unei comenzi slash din output-ul unui hook, in tot hooks.md (3840 linii) si hooks-guide.md (1064 linii) — nu exista. Singurele cai care declanseaza efectiv o compactare/clear raman actiunile native ale utilizatorului (/clear, /compact) sau host-ul/SDK-ul care porneste sesiunea.


6. Stop hook — poate bloca oprirea si injecta o instructiune?

Da, in doua moduri diferite, sursa https://code.claude.com/docs/en/hooks.md, ### Stop + #### Stop input + #### Stop decision control:

Input:

In addition to the common input fields, Stop hooks receive stop_hook_active, last_assistant_message, background_tasks, and session_crons. The stop_hook_active field is true when Claude Code is already continuing as a result of a stop hook. Check this value or process the transcript to avoid blocking on a condition that will never resolve. Claude Code overrides the hook and ends the turn after 8 consecutive blocks.

Decision control:

Stop and SubagentStop hooks can control whether Claude continues. In addition to the JSON output fields available to all hooks, your hook script can return these event-specific fields:

Field Description
decision "block" prevents Claude from stopping. Omit to allow Claude to stop
reason Required when decision is "block". Tells Claude why it should continue
hookSpecificOutput.additionalContext Non-error feedback for Claude. The conversation continues so Claude can act on it, but unlike decision: "block" it is shown in the transcript as hook feedback rather than a hook error

A hook that blocks by exiting 2 routes the same way as reason: Claude receives the stderr message as the explanation for why it should continue.

{
  "decision": "block",
  "reason": "Must be provided when Claude is blocked from stopping"
}

Use additionalContext when the hook is working as designed and giving Claude guidance, such as "run the test suite before finishing". It keeps the conversation going through the same loop protections as decision: "block", namely the stop_hook_active input and the 8-consecutive-continuation cap, but the transcript labels it Stop hook feedback and no hook error notification is shown.

Deci: decision: "block" + reason (top-level, NU in hookSpecificOutput) forteaza continuarea si arata reason ca eroare de hook; hookSpecificOutput.additionalContext face acelasi lucru dar apare ca "Stop hook feedback", nu ca eroare. Ambele variante trec prin aceleasi limite de bucla: campul stop_hook_active (hook-ul trebuie sa verifice singur ca sa nu se blocheze la infinit) si plafonul intern de 8 blocari consecutive fara progres, dupa care Claude Code forteaza oprirea oricum.

Exit code 2 pe Stop: din tabelul citat separat in hooks.md, poate bloca (spre deosebire de SessionStart, unde exit 2 doar arata mesajul utilizatorului si continua executia).

Aplicabil direct la ideea "Stop scrie handoff-ul acum": da, se poate implementa — hook-ul Stop verifica o conditie (context mare, sarcina neterminata etc.), daca handoff-ul nu exista inca pe disc raspunde cu decision: "block" + reason: "scrie handoff-ul pe disc inainte de oprire", Claude continua turul, scrie handoff-ul, iar hook-ul (cand ruleaza din nou la urmatoarea incercare de Stop) vede handoff-ul pe disc si lasa oprirea sa treaca. Trebuie verificat stop_hook_active ca sa nu intre in bucla si respectat plafonul de 8.


VERDICT PRACTIC

Lantul complet "handoff pe disc -> /clear -> reinjectare automata" se poate construi, cu doua completari fata de premisa initiala:

  1. Partea automata, confirmata:

    • SessionStart cu matcher clear (sau, pentru compactare, matcher compact) fireste dupa /clear/compactare, primeste source explicit ("clear" / "compact") — hook-ul poate citi handoff-ul de pe disc si il injecta prin hookSpecificOutput.additionalContext, INAINTE de primul prompt al sesiunii noi. Asta merge din prima, fara nicio interventie manuala suplimentara, pentru ambele declansatoare (/clear SI compactare — nu doar /clear).
    • Nu exista limita blocanta de marime: sub 10.000 caractere textul intra direct in context; peste, Claude Code il scrie singur intr-un fisier si trimite calea — deci un handoff mare nu se pierde, doar se livreaza indirect.
    • Stop poate fi folosit ca plasa de siguranta care FORTEAZA scrierea handoff-ului inainte de oprire (decision: "block" + reason), cu conditia sa respecte stop_hook_active si plafonul de 8 blocari.
  2. Ce ramane obligatoriu manual / in afara hook-urilor:

    • Niciun hook nu poate declansa /clear sau /compact singur — confirmat explicit in documentatie ("can't trigger / commands or tool calls"). Utilizatorul (sau orchestrator-ul, daca ruleaza in headless/SDK cu control asupra sesiunii) trebuie sa emita el actiunea.
    • Niciun hook nu anunta "am ajuns la ~50% context" — nu exista un eveniment de tip "context threshold reached". Campurile context_tokens etc. apar DOAR pe SessionStart cu source: "resume"/"fork", dupa fapt — nu in timp real, in timpul sesiunii curente. Decizia de "e timpul sa scriu handoff-ul" ramane a modelului/sesiunii, exact cum descrie deja regula din CLAUDE.md — hook-urile nu o pot automatiza.
    • Premisa gresita de reparat: daca planul se baza pe PostCompact pentru reinjectare, nu functioneaza — PostCompact n-are decision control deloc. Mecanismul corect pentru compactare e SessionStart cu matcher compact, nu PostCompact.

Pe scurt: partea "citeste handoff de pe disc si baga-l inapoi in context la (re)pornire" e 100% automata prin SessionStart. Partea "declanseaza tu insuti /clear" ramane 100% manuala — nu exista ocolire documentata. Stop poate automatiza doar "nu te opri pana nu ai scris handoff-ul pe disc", nu si declansarea lui /clear dupa aceea.


Nota metodologica

WebFetch (tool-ul standard) trunchiaza/rezuma paginile hooks si hooks-guide inainte sa ajunga la sectiunile per-eveniment (confirmat empiric: trei incercari diferite de prompt au esuat sa extraga ### SessionStart, ### Stop, ### PostCompact din pagina hooks, desi tabelele de sus ale paginii ies corect). Documentatia Mintlify expune si varianta bruta: https://code.claude.com/docs/en/hooks.md si .../hooks-guide.md (mentionate chiar de pagina: "Fetch the complete documentation index at: https://code.claude.com/docs/llms.txt"). Am fetch-uit acele .md-uri direct prin curl (citire read-only, fara nicio scriere in afara acestui livrabil) si am citat din ele. Toate citatele de mai sus sunt verbatim din acele fisiere.