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

12 KiB

Verificare "function hooks" — Claude Code 2.1.274 (nativ, win32-x64)

Sursa de verificat: https://claudefa.st/blog/tools/hooks/function-hooks (blog tert, neoficial). Metoda: (1) incercare de rulare reala /plugin-types; (2) cautare de siruri si context in binarul nativ instalat, C:\Users\mmari\.local\bin\claude.exe (233691808 octeti, Bun-compiled, comitul 1efcc1361e64). Fiecare constatare de mai jos e marcata cu sursa ei.

1. Rularea comenzii cerute

CLAUDE_CODE_ENABLE_FUNCTION_HOOKS=1 claude -p "/plugin-types ./types"

Rezultat: nu s-a generat niciun fisier. Raspunsul modelului a fost text conversational ("Ready — no task given yet." / la reincercare cu --output-format json: "I'm ready. What would you like me to work on?"). Niciun folder types/ sau .claude/types/ nu a aparut in scratchpad dupa rulare (verificat cu ls).

Cauza identificata, nu specifica function hooks: am testat control cu claude -p "/help" (comanda locala cea mai de baza din tot CLI-ul, intotdeauna inregistrata, fara nicio poarta de feature-flag). A esuat identic — modelul a primit "/help" ca text mangled si a raspuns conversational, in loc sa afiseze ajutorul. Concluzie: invocarea claude -p "<text>" din acest mediu nu ruteaza deloc prin dispecerul de comenzi locale ("slash commands") — cererea merge direct la model ca prompt text. Din --debug-file, header-ul de atribuire arata cc_entrypoint=sdk-cli, adica sesiunea porneste prin calea SDK, nu prin REPL-ul interactiv complet; acolo comenzile locale par sa nu fie interceptate. Deci esecul lui /plugin-types nu e dovada ca function hooks sunt dezactivate — e o limitare a modului de invocare folosit aici, valabila si pentru comenzi complet neutre ca /help. N-am putut testa varianta interactiva (fara TTY in acest mediu).

claude --help si claude plugin --help (rulate separat, capturate integral) nu listeaza nicio comanda plugin-types la nivel de CLI top-level — motivul e ca /plugin-types e o comanda in-sesiune ("slash command"), nu o subcomanda a executabilului claude, deci absenta ei din --help e normala si nu spune nimic despre activare.

2. Ce exista REAL in binar (cautare de siruri, cu offset de octet verificabil)

Nu s-au generat .d.ts, deci n-am putut citi declaratii TypeScript complete ca "dovada canonica" ceruta in sarcina. In schimb, am gasit in binar cod sursa minificat (nu doar siruri izolate) care confirma mecanismul de facto. Tot ce urmeaza e citat verbatim din binar, cu offset.

2a. Flag-ul si poarta lui reala

La offset ~103270720 (grep -a -b), blocul de cod al flag-ului:

var kYe="tengu_plugin_hooks_modules";
var Azt=()=>!1;
var AFe=()=>a.CLAUDE_CODE_ENABLE_FUNCTION_HOOKS??P(kYe,Azt());
var h8=()=>AFe()&&!qb()&&!Br("hooks")&&!zm();

Interpretare directa din cod:

  • flag-ul GrowthBook real se numeste tengu_plugin_hooks_modules, default oprit (Azt=()=>!1).
  • CLAUDE_CODE_ENABLE_FUNCTION_HOOKS e o suprascriere locala peste acel flag (??) — confirma exact ce zice blogul despre numele variabilei de mediu.
  • dar poarta finala folosita in productie, h8(), cere si !qb() && !Br("hooks") && !zm() — trei conditii suplimentare. Numele acestor functii sunt minificate si reciclate in zeci de module din bundle (acelasi nume qb/Br/zm apare cu corpuri complet diferite in alte scope-uri), deci nu pot atribui cu certitudine ce verifica exact aici fara deminificare completa — onest: aceasta e limita cercetarii, nu o concluzie.
  • Alaturi, la acelasi offset, un tabel de atribuire a sursei flag-ului pentru afisare in UI: {override:"from a local override", payload:"from GrowthBook (this session's payload)", disk:"from GrowthBook (the disk cache of an earlier session)", disabled:"from the default (GrowthBook is off ...)"} — confirma ca e un flag GrowthBook cu override local, exact mecanismul descris de blog.

2b. Comanda /plugin-types — confirmata REAL, dar NEgatata de flag

La offset 203438732, definitia completa a comenzii (citat verbatim):

var mer=Object.freeze({type:"local",name:"plugin-types",
  description:"Write claude-code.d.ts, claude-code-plugins.d.ts and claude-code-mcp.d.ts:
  the plugin API's TypeScript declarations, the enabled plugins' type contracts and the
  inputs of the connected MCP tools, for typing a hooks module against this session",
  argumentHint:"[dir]", supportsNonInteractive:!0,
  load:()=>import("B:/~BUN/root/chunk-0rpsm23r.js")});

Important: acest obiect n-are nicio conditie isEnabled/gate legata de h8() sau de flag — e inregistrata neconditionat. Descrierea confirma explicit sintagma "hooks module" din blog: comanda exista ca sa tipizeze "a hooks module against this session". Deci comanda in sine e reala si documentata intern, indiferent de starea flag-ului; problema empirica de mai sus (sectiunea 1) e doar despre modul de invocare -p din acest mediu.

2c. Lista REALA de evenimente si API-uri ("scan manifest"), gasita ca date, nu ca documentatie

La offset 223160560 exista un manifest static de tip "scan" folosit (aparent) pentru analiza statica / sandboxing a unui modul de plugin, cu lista completa si explicita:

scan: {
  hooks: ["session.start","ui.render","command.run","ui.close","ui.focus","ui.scroll",
          "tool.call","prompt.submit"],
  calls: ["clock.after","clock.every","clock.now","command.register","fs.list","fs.read",
          "fs.stat","process.run","session.id","session.messages","store.get","store.set",
          "telemetry.log","telemetry.mark","ui.close","ui.invalidate","ui.log","ui.open",
          "ui.resolve","ui.status"]
}

Aceasta e cea mai tare dovada gasita — e o lista de date, nu un string de proza, deci foarte probabil chiar suprafata reala (sau foarte apropiata) a API-ului de hooks module:

  • evenimente de hook confirmate: session.start, ui.render, command.run, ui.close, ui.focus, ui.scroll, tool.call, prompt.submit (8 la numar).
  • turn.complete si turn.start NU sunt in lista de evenimente de hook, desi cele doua siruri exista in alta parte a binarului (24, respectiv 68 aparitii) — apartin mecanismului intern de motor ("engine turn 1 start/end", vazut si in log-ul de debug la rularea reala), nu suprafetei de hooks expuse pluginurilor.
  • prompt.submit e confirmat, si separat, la offset 206959908, exista codul intern real care implementeaza acest hook point: un pipeline core/managed cu posibilitate de a rescrie textul promptului ("prompt.submit: text rewritten by a hook (...)") sau de a-l bloca ("Prompt dropped by a hook: ..."). Semnatura exacta gasita e apelul .prompt.submit({submission, origin, turnId, shouldWait}) — nu am gasit litera $ ca alias/receiver exact in siruri; forma $.prompt.submit({text}) din blog e plauzibila ca sugar-syntax peste acest mecanism, dar nu e confirmata verbatim.
  • API-uri de tip "calls" confirmate ca namespace-uri cu metode: fs.read/list/stat (deci $.fs probabil e un namespace, nu o valoare simpla), store.get/set, clock.after/every/now, session.id/messages — coincide cu ce cerea sarcina sa verific ($.session.messages(), $.fs, $.store, $.clock.every), dar iar, prefixul $. in sine nu apare ca sir literal langa aceste nume — e o inferenta rezonabila, nu o citare directa.

2d. Tokeni / context / usage / compactare — CAUTATE EXPLICIT, NU GASITE

Am cautat explicit token, usage, context, compact in vecinatatea manifestului de mai sus si in restul zonei de "function hooks". Niciunul dintre cele 20 de calls sau 8 hooks de mai sus nu are legatura cu tokeni, context window, usage sau compactare. Sirurile contextWindow (8 aparitii) si compact (405 aparitii) exista din abundenta in binar, dar in alte module (afisarea usage-ului in --output-format json, autocompact intern al motorului — vazut si in logul de debug: autocompact: tokens=[REDACTED] level=ok effectiveWindow=980000), nu ca hook sau call disponibil unui modul de function hooks. Nu exista dovada in acest binar ca un modul de hooks ar putea citi tokenii ramasi sau starea de compactare.

2e. Forma register si hooks/hooks.json

Doua descoperiri, ambele reale, dar doua lucruri diferite:

  1. hooks.json din binar (27 aparitii) e manifestul VECHI/existent de plugin-hooks (PreToolUse, SessionStart etc. — cel folosit deja de pluginul ponytail din acest proiect, vazut in logul de debug: Read manifest hooks for plugin ponytail (enabled=true): ./hooks/claude-codex-hooks.json). Contextul din binar confirma: sirul e "plugin hooks.json", langa mesaje despre SKILL.md si actualizarea regulilor de auto-mode/permisiuni — sistemul clasic de hooks pe evenimente de tool-use, nesuprapus cu "function hooks".

  2. Forma register pentru "function hooks" (modulul nou), gasita in doua exemple interne reale de pluginuri deja construite cu acest mecanism:

    • tengu_quiet_dolphin — "panoul de diff ca plugin: /diff", cu isAvailable:()=>At unde At=()=>h8()&&lu(Rr(),bt) — confirma ca h8() (poarta de function hooks descrisa la 2a) chiar gateaza un plugin real deja construit, nu doar un flag mort.
    • tengu_tips_mod — "spinner tips as a plugin", cu forma exacta:
      var P=(e)=>({register:(o)=>{e.registerHooks(o,x())}});
      
      adica modulul exporta un register(engine) care apeleaza engine.registerHooks(implementareHooks, apiSuplimentar). E cea mai apropiata dovada gasita de "forma exacta a functiei register", dar e exemplul unui plugin intern concret, nu semnatura generica documentata explicit undeva ca atare.

VERDICT

Se poate construi o bucla "prag context -> scrie handoff -> /clear -> reia din handoff" complet automat, FARA interventia utilizatorului, folosind acest surface?

Nu, nu cu ce am vazut efectiv in acest binar. Motive, punct cu punct:

  1. Suprafata de hooks confirmata (session.start, ui.*, command.run, tool.call, prompt.submit) nu contine niciun eveniment sau apel legat de prag de context, tokeni, usage sau compactare (sectiunea 2d) — deci un modul de hooks n-ar avea de unde sa afle "am trecut de 200k tokeni" din interior. Ar trebui sa deduca asta indirect (ex. numarand caractere din session.messages()), fara nicio garantie ca se potriveste cu tokenizarea reala sau cu autocompact intern.
  2. Nu exista niciun hook de tipul "inainte de compactare" / "sesiune se inchide" in lista — deci nu exista un punct de agatare curat pentru "scrie handoff chiar inainte sa se piarda contextul".
  3. /clear (sau relansarea sesiunii) nu apare in calls-urile confirmate (command.register exista, dar a inregistra o comanda noua nu e totuna cu a declansa /clear programatic din interiorul unui hook).
  4. Flag-ul e in continuare oprit implicit (tengu_plugin_hooks_modules default !1), marcat "early access" chiar in textul din comanda /plugin-types insasi ("module 'claude-code', early access: it may change between releases") — deci si daca API-ul de mai sus ar acoperi cazul, Anthropic il trateaza explicit ca nestabil.
  5. Nu am reusit sa validam empiric nimic din surface (n-am putut genera .d.ts din cauza limitarii de mediu descrise la sectiunea 1) — tot ce e mai sus e din citirea codului minificat, nu din declaratii TypeScript generate si citite ca atare, cum cerea procedura.

Ce ar lipsi/ar trebui reincercat ca sa se stie sigur: rulare interactiva reala (TTY, nu -p/SDK) cu CLAUDE_CODE_ENABLE_FUNCTION_HOOKS=1, ca sa se vada daca /plugin-types chiar scrie fisierele si daca h8() se evalueaza true in acel context — asta ar da acces la declaratiile TypeScript reale si ar inchide toate necunoscutele de mai sus (in special conditiile qb()/Br("hooks")/zm() neatribuite cu certitudine).