fix(infra): temp IdeA géré alloué avant le convention file, exposé via IDEA_MANAGED_TMP et instruction explicite privilégiant le temp géré à /tmp — #200

This commit is contained in:
2026-08-22 09:50:00 +02:00
parent 3344361fc2
commit f8cf86c6f5
4 changed files with 120 additions and 47 deletions

View File

@ -2278,18 +2278,42 @@ impl LaunchAgent {
})
.await;
}
self.apply_injection(
&input.project,
&agent.context_path,
&effective_context,
profile.mcp.is_some(),
&mut spec,
)
.await?;
let local_model_server_guard = self
.ensure_local_model_server_for_opencode(&input.project, &agent, &mut profile)
let launch_managed_temp = self
.allocate_launch_managed_temp(&input.project, &agent)
.await?;
if let Some(temp) = &launch_managed_temp {
upsert_env(&mut spec.env, "TMPDIR", &temp.path);
upsert_env(&mut spec.env, "TMP", &temp.path);
upsert_env(&mut spec.env, "TEMP", &temp.path);
upsert_env(&mut spec.env, "IDEA_MANAGED_TMP", &temp.path);
}
if let Err(err) = self
.apply_injection(
&input.project,
&agent.context_path,
&effective_context,
profile.mcp.is_some(),
launch_managed_temp.as_ref().map(|temp| temp.path.as_str()),
&mut spec,
)
.await
{
self.release_launch_managed_temp_best_effort(launch_managed_temp.as_ref())
.await;
return Err(err);
}
let local_model_server_guard = match self
.ensure_local_model_server_for_opencode(&input.project, &agent, &mut profile)
.await
{
Ok(guard) => guard,
Err(err) => {
self.release_launch_managed_temp_best_effort(launch_managed_temp.as_ref())
.await;
return Err(err);
}
};
// 5a. ── INJECTION DE LA CONF MCP (cadrage v3, Décision 3) ──
// Strictement APRÈS le convention file (étape 5) et AVANT le spawn /
@ -2297,15 +2321,21 @@ impl LaunchAgent {
// IdeA matérialise SA config MCP au format de CETTE CLI, dans le **même**
// run dir isolé que le convention file et le seed de permissions. `None`
// ⇒ aucun write/flag/env (chemin actuel inchangé, zéro régression).
self.apply_mcp_config(
&profile,
&run_dir,
&input.project.root,
input.mcp_runtime.as_ref(),
effective_permissions.as_ref(),
&mut spec,
)
.await?;
if let Err(err) = self
.apply_mcp_config(
&profile,
&run_dir,
&input.project.root,
input.mcp_runtime.as_ref(),
effective_permissions.as_ref(),
&mut spec,
)
.await
{
self.release_launch_managed_temp_best_effort(launch_managed_temp.as_ref())
.await;
return Err(err);
}
// 5c. ── PROJECTION DES PERMISSIONS (lot LP3-3) ──
// Strictement APRÈS le convention file (5) ET la conf MCP (5a), donc
@ -2313,15 +2343,21 @@ impl LaunchAgent {
// héritent de la projection (fichiers du plan écrits dans le run dir,
// `args`/`env` foldés dans `spec`). `None` registre / profil non
// projetable / `eff == None` ⇒ no-op (prompting natif conservé).
self.apply_permission_projection(
&profile,
&run_dir,
&input.project.root,
effective_permissions.as_ref(),
network_permission,
&mut spec,
)
.await?;
if let Err(err) = self
.apply_permission_projection(
&profile,
&run_dir,
&input.project.root,
effective_permissions.as_ref(),
network_permission,
&mut spec,
)
.await
{
self.release_launch_managed_temp_best_effort(launch_managed_temp.as_ref())
.await;
return Err(err);
}
// 5d. ── PLAN DE SANDBOX OS (lot LP4-3) ──
// Strictement APRÈS la résolution des permissions (3) et la projection
@ -2331,14 +2367,6 @@ impl LaunchAgent {
// remplir `spec.sandbox`. `eff == None` (rien posé) ⇒ `None` : aucun plan,
// comportement natif conservé (invariant produit). L'enforcer concret
// (Landlock/Noop) est injecté côté PTY au composition root.
let launch_managed_temp = self
.allocate_launch_managed_temp(&input.project, &agent)
.await?;
if let Some(temp) = &launch_managed_temp {
upsert_env(&mut spec.env, "TMPDIR", &temp.path);
upsert_env(&mut spec.env, "TMP", &temp.path);
upsert_env(&mut spec.env, "TEMP", &temp.path);
}
let exposed_managed_temp_roots = match self
.resolve_managed_temp_exposure(&input.managed_temp_exposure)
.await
@ -2916,6 +2944,7 @@ impl LaunchAgent {
context_rel_path: &str,
effective: &EffectiveAgentContext,
mcp_enabled: bool,
launch_managed_temp_path: Option<&str>,
spec: &mut SpawnSpec,
) -> Result<(), AppError> {
match spec.context_plan.clone() {
@ -2927,8 +2956,12 @@ impl LaunchAgent {
// composed: an absolute project-root header (so the agent knows
// where to operate, since its cwd is *not* the root), the agent's
// persona `.md`, then the bodies of its assigned skills (§14.2).
let document =
compose_convention_file(project.root.as_str(), effective, mcp_enabled);
let document = compose_convention_file(
project.root.as_str(),
effective,
mcp_enabled,
launch_managed_temp_path,
);
let path = RemotePath::new(join(&spec.cwd, &target));
self.fs.write(&path, document.as_bytes()).await?;
}
@ -3864,6 +3897,7 @@ pub(crate) fn compose_convention_file(
project_root: &str,
effective: &EffectiveAgentContext,
mcp_enabled: bool,
launch_managed_temp_path: Option<&str>,
) -> String {
let mut out = String::new();
out.push_str("# Project root\n\n");
@ -3949,6 +3983,12 @@ pub(crate) fn compose_convention_file(
);
}
out.push_str(skill_awareness(mcp_enabled));
if let Some(path) = launch_managed_temp_path
.map(str::trim)
.filter(|path| !path.is_empty())
{
out.push_str(&managed_temp_awareness(path));
}
out.push_str(memory_awareness());
out.push_str("---\n\n");
@ -4087,6 +4127,14 @@ fn memory_awareness() -> &'static str {
réutilise le même `slug` pour remplacer une note existante.\n\n"
}
fn managed_temp_awareness(path: &str) -> String {
format!(
"**Temporaire IdeA géré** : utilise `{path}` pour les gros fichiers temporaires, \
clones, caches et artefacts de travail. Ce chemin est aussi exposé via \
`IDEA_MANAGED_TMP`, `TMPDIR`, `TMP` et `TEMP`; privilégie-le à `/tmp`.\n\n"
)
}
/// Renders a [`MemoryType`] as its stable lowercase label for the convention-file
/// memory section (`user`/`feedback`/`project`/`reference`).
#[must_use]
@ -4182,7 +4230,7 @@ mod tests {
handoff: handoff.cloned(),
live_rows: live_rows.to_vec(),
};
super::compose_convention_file(project_root, &effective, mcp_enabled)
super::compose_convention_file(project_root, &effective, mcp_enabled, None)
}
#[test]