sync SVN r18174
This commit is contained in:
330
docs/sessionstart_ref.md
Normal file
330
docs/sessionstart_ref.md
Normal file
@@ -0,0 +1,330 @@
|
||||
\# 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](#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](#json-output) 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... |
|
||||
>
|
||||
> ```json
|
||||
> {
|
||||
> "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:
|
||||
|
||||
> * [SessionStart](#sessionstart) and [SubagentStart](#subagentstart): at the start of the
|
||||
> conversation, before the first prompt
|
||||
|
||||
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.
|
||||
>
|
||||
> ```json
|
||||
> {
|
||||
> "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.
|
||||
>
|
||||
> ```json
|
||||
> {
|
||||
> "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.
|
||||
Reference in New Issue
Block a user