Files
Loki/internal/loki/facade_projsnap.go
T
Claude ad52da18a9 Lot 34 : bloc projet figé (PROJ_SNAPSHOT) dans le tour de la façade
PROJ_SNAPSHOT était le dernier réglage qui gardait la conversation au
Go : la façade la tient désormais dans tous les cas (LOKI_CHAT=go reste
la porte de sortie). facadeChatBlocker et la décision prise une fois par
processus disparaissent.

Go (facade_projsnap.go) :
- /internal/v1/turn/view, avec la clé et l'agent : tête du tour portant
  le bloc figé (ou le vivant à rafraîchir), préambule sans date ni
  dossier, et la décision projsnap (rafraîchir, mise à jour à poser,
  instantané à ranger, clé et système du tour) ; dry pour le
  préchauffage ;
- /internal/v1/turn/projsnap/refresh : bloc vivant et instantané qu'il
  devient, pour une compaction en plein tour.

Rust :
- ChatService::assemble_turn, partagé par le tour et la relance du
  builder : instantané rangé avec la discussion, mise à jour posée en tête
  du dernier message utilisateur (prependCtxUpdate), historique nettoyé
  au rafraîchissement ;
- TurnHost::project_refresh après une compaction ou une réduction en
  plein tour (replaceProjectHead) ; l'instantané posé devient celui de la
  discussion ;
- le préchauffage demande la vue en dry et garde les mises à jour tant
  que le bloc n'est pas rafraîchi.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_016tcGfJWefW5kUGoC2wL8cv
2026-10-16 03:13:27 +00:00

95 lines
3.2 KiB
Go

package loki
import (
"net/http"
)
// facade_projsnap.go — PROJ_SNAPSHOT pour le tour tenu par la façade (lot 34
// de la migration). La façade range l'instantané avec la discussion (il est
// persisté avec elle, champ proj_snap) et pose les <context_update> dans son
// historique ; le Go, qui lit le contexte du projet (description, index
// mémoire, trackers, consignes du dépôt, date et dossier de travail), décide :
//
// - /internal/v1/turn/view, avec la clé et l'agent : la tête du tour porte
// le bloc figé (ou le vivant s'il faut rafraîchir), et projsnap dit quoi
// faire de la discussion — rafraîchir (instantané neuf, blocs retirés de
// l'historique) ou poser une mise à jour en tête du dernier message
// utilisateur. dry : la même décision pour le préchauffage, sans rien
// rendre à ranger ;
// - /internal/v1/turn/projsnap/refresh : une compaction ou une réduction
// réécrit l'historique en plein tour, le bloc vivant remplace le figé et
// devient l'instantané du tour suivant.
// projSnapView : les pièces de turnView avec la clé (assembleTurn, chemin
// PROJ_SNAPSHOT). snap : l'instantané de la discussion (nil : aucun).
func projSnapView(caps Caps, snap *projSnapshot, dry bool) map[string]any {
cfg := ReadConfig()
// Date et dossier de travail : dans le bloc, plus dans le système.
caps.envInCtx = true
asOf := projSnapNow()
live := projSnapCapture(caps, asOf)
key := projSnapKey(caps, cfg)
tools := EnabledTools(caps)
if tools == nil {
tools = []Tool{}
}
head := []Message{}
if sp := readSysPrompt(); sp != "" {
head = append(head, Message{Role: "system", Content: sp})
}
sysHash := projSnapSysHash(InjectSkills(append([]Message(nil), head...), caps, tools), tools)
refresh, delta, tok := projSnapDecide(snap, live, key, sysHash, asOf)
block := live.msgs
if !refresh {
block = snap.Msgs
}
out := map[string]any{"refresh": refresh, "key": key, "sys_hash": sysHash}
if !dry {
switch {
case refresh:
out["snap"] = newProjSnapshot(live, key, sysHash)
case delta != "":
// Rangé par la façade seulement si la mise à jour a trouvé un
// message utilisateur où se poser (projSnapApplyLocked).
next := *snap
next.State = live.state
next.DeltaTok += tok
out["snap"] = &next
out["delta"] = delta
default:
out["snap"] = snap
}
}
return map[string]any{
"head": append(head, block...),
"prefix": injectPrefix(caps, tools),
"tools": tools,
"projsnap": out,
}
}
// handleInternalTurnProjSnapRefresh : POST {"caps","key","sys_hash"} →
// {"block":[…],"snap":{…}} — le bloc vivant et l'instantané qu'il devient
// (projSnapTurn.refresh).
func handleInternalTurnProjSnapRefresh(w http.ResponseWriter, r *http.Request) {
var in struct {
Caps Caps `json:"caps"`
Key string `json:"key"`
SysHash string `json:"sys_hash"`
}
if !readInternalJSON(w, r, &in) {
return
}
caps := in.Caps
caps.envInCtx = true
live := projSnapCapture(caps, projSnapNow())
block := live.msgs
if block == nil {
block = []Message{}
}
sendJSON(w, http.StatusOK, map[string]any{
"block": block,
"snap": newProjSnapshot(live, in.Key, in.SysHash),
})
}