Files
Loki/internal/loki/chat_envctx.go
T
MichaelandClaude Opus 5.5 c888afb493 Contexte : avec PROJ_SNAPSHOT, la date et le dossier de travail passent dans le bloc figé
Le système portait deux valeurs qui bougent : la date du jour — à minuit,
toute discussion en cours recalculait son prompt entier — et le dossier de
travail, propre à chaque discussion, si bien que deux discussions ne
partageaient même pas leur système. Avec PROJ_SNAPSHOT, ces valeurs
rejoignent le bloc projet figé, en tête du premier message ; le système ne
garde que les consignes (« ton dossier de travail est dans ton contexte »).

- Bloc « Environment (from Loki) » : date (avec l'année, et l'année périmée
  que la consigne web nommait), dossier de la discussion, ou poste distant
  ciblé (nom, système, dossier, hors ligne).
- Contenu toujours vivant : relu à chaque tour ; un changement de jour part en
  une ligne de <context_update>, tout autre écart (poste hors ligne, autre
  cible) renvoie le message entier. L'avertissement « hors ligne » ne fige
  donc plus le système.
- Hôte, compte et dossier des scripts, les mêmes pour toutes les discussions,
  restent dans le système.
- Seul l'assemblage d'un tour de discussion avec la clé pose caps.envInCtx :
  tâches planifiées, sous-agents, vérification et terminal gardent leur
  système, date et dossier compris.
- Sans la clé : système identique à l'octet (gabarits relevés sur le code
  d'avant, test). Format d'instantané passé à 2 : les instantanés persistés
  sont repris neufs.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-04 14:08:07 +02:00

117 lines
4.2 KiB
Go

package loki
// chat_envctx.go — PROJ_SNAPSHOT : la date et le dossier de travail sortent du
// prompt système pour entrer dans le bloc projet figé de la discussion.
//
// Pourquoi. Le système (préambule + briefing machine) et les outils forment le
// début de chaque requête ; tant qu'ils ne bougent pas, le moteur reprend son
// cache. Deux valeurs y bougeaient pourtant : la date — à minuit, tout le prompt
// d'une discussion en cours est recalculé — et le dossier de travail, propre à
// chaque discussion — deux discussions ne partageaient donc rien, pas même le
// système. Avec la clé, le système ne garde que des consignes (« ton dossier de
// travail est dans ton contexte ») et les valeurs partent dans le bloc projet,
// en tête du premier message utilisateur, avec le reste du contexte figé.
//
// Rien n'est mémorisé au-delà du bloc lui-même : chaque tour relit la date, le
// dossier et la cible, et tout écart part en <context_update> dans le message
// suivant — une ligne pour un changement de jour, le bloc entier sinon (poste
// distant passé hors ligne, autre cible, autre dossier). Les mêmes faits, à une
// autre place ; aucun n'est retenu par Loki plus longtemps qu'avant.
//
// Seul un tour de discussion avec la clé y passe (assembleTurn pose
// caps.envInCtx). Une tâche planifiée, un sous-agent, la vérification du mode
// Code, le terminal gardent leur système d'avant, date et dossier compris.
import (
"strings"
"time"
)
// envContextPrefix ouvre le message (isProjectSystem le range avec le contexte
// du projet). Assez particulier pour qu'aucun prompt de preset ne commence ainsi.
const envContextPrefix = "Environment (from Loki)"
// nodeOfflineLine : l'avertissement d'un poste distant hors ligne — dans le
// système sans la clé, dans le bloc projet avec.
const nodeOfflineLine = "⚠ It is currently OFFLINE: those tools will fail until it reconnects. Tell the user instead of trying repeatedly."
// envNow : l'horloge du bloc (remplaçable en test).
var envNow = time.Now
// envDateLine : la date du jour, avec l'année et l'année périmée que la
// consigne web du système désignait jusque-là par leur valeur.
func envDateLine(day string) string {
year := day
if len(day) >= 4 {
year = day[:4]
}
return "Date: " + day + " (the current year is " + year + ", not " + prevYear(year) + ")."
}
// envMachineFacts : les valeurs du briefing machine qui varient — dossier de la
// discussion, ou poste distant (nom, système, dossier, état). "" sans agent.
// Les consignes qui les accompagnent restent dans machineSystemPrompt.
func envMachineFacts(caps Caps) string {
if !caps.Agent {
return ""
}
if tgt, ok := nodeTargetMetaGet(); ok {
s := "Machine: REMOTE node " + tgt.name
if tgt.os != "" {
s += " (" + tgt.os + ")"
}
if tgt.root != "" {
s += ", working folder " + tgt.root
}
s += "."
if !tgt.connected {
s += " " + nodeOfflineLine
}
return s
}
if cwd := agentCwd(); cwd != "" {
return "Working folder (cwd): " + cwd + "."
}
return ""
}
// envCapture : la date (si le préambule en portait une) et les valeurs machine
// de ce tour.
func envCapture(caps Caps) (day, facts string) {
if caps.Agent || caps.Mem != MemOff {
day = envNow().Format("2006-01-02")
}
return day, envMachineFacts(caps)
}
// envContextMessage : le message du bloc, ok=false s'il n'a rien à dire.
func envContextMessage(day, facts string) (Message, bool) {
var lines []string
if day != "" {
lines = append(lines, envDateLine(day))
}
if facts != "" {
lines = append(lines, facts)
}
if len(lines) == 0 {
return Message{}, false
}
return Message{Role: "system", Content: envContextPrefix + ":\n" + strings.Join(lines, "\n")}, true
}
// envDelta : la mise à jour entre l'environnement annoncé et le vivant. Un
// changement de jour seul tient en une ligne ; tout autre écart renvoie le
// message entier.
func envDelta(oldDay, oldFacts, day, facts string) string {
switch {
case oldFacts == facts && oldDay == day:
return ""
case oldFacts == facts && day != "" && oldDay != "":
return "New day — " + envDateLine(day) + "\n"
}
if m, ok := envContextMessage(day, facts); ok {
return "Current version, replaces any earlier one — " + msgText(m) + "\n"
}
return "Environment: removed.\n"
}