agent_profile/adapter/
amp.rs1use std::path::PathBuf;
11
12use super::{
13 Adapter, AdapterEvidence, AdapterMetadata, Capability, CapabilityClaim, CapabilityState,
14 ConflictOption, Mechanism, PathKind, PlanContext, PlannedLaunch, SupportLevel, profile_dir,
15};
16use crate::config::AppRoot;
17use crate::error::Result;
18use crate::name::ProfileName;
19
20const FLAG: &str = "--settings-file";
21const FILE_NAME: &str = "settings.json";
22
23const SETTINGS_OPTION: ConflictOption = ConflictOption { long: &[FLAG], short: None };
27
28pub const INITIAL_CONFIG: &[u8] = b"";
32
33pub const LAYERING_NOTE: &str = "--settings-file selects the user settings file, which workspace settings override and managed settings override both";
35
36static METADATA: AdapterMetadata = AdapterMetadata {
37 id: "amp",
38 executable: "amp",
39 mechanism: Mechanism::FlagFile(SETTINGS_OPTION),
40 support: SupportLevel::Proven,
41 evidence: AdapterEvidence {
42 mechanism_id: "amp-settings-file-v1",
43 verified_at: "2026-10-25",
44 upstream_version: "0.0.1792872041-gb83e0b",
45 source_url: "https://ampcode.com/docs/cli/settings",
46 notes: "measured in a sandbox at 0.0.1792872041-gb83e0b: a launch with --settings-file exits 0, \
47 but the agent's own writes did not follow it — `accounts.json` and `device-id.json` were \
48 written under the default `.local/share/amp` while the flag pointed at the probe's target \
49 (§9 outcome 5), and the default `.config/amp` was `(absent)` at baseline and stayed so; \
50 the candidate sweep accepted the empty file and {} (all exit 0); the strings scan found \
51 AMP_API_KEY, AMP_URL and AMP_SETTINGS_FILE documented, and `--settings-file` itself does \
52 not appear verbatim in the truncated help capture — the acceptance evidence is the \
53 exit-0 use of the flag",
54 },
55 capabilities: &[
56 CapabilityClaim {
57 capability: Capability::ConfigIsolation,
58 state: CapabilityState::NotGuaranteed,
59 basis: "measured: the flag was accepted (exit 0) but moved nothing to the target — \
60 `+ f /home/agent/.local/share/amp/device-id.json` was written at the default, and \
61 `.local/share/amp` was the only observed write target of the launch, while the \
62 target's `settings.json` was the probe's own file and stayed the only file there",
63 },
64 CapabilityClaim {
65 capability: Capability::CredentialIsolation,
66 state: CapabilityState::Unknown,
67 basis: "unmeasured: no credential file was observed anywhere in the delta and no login step \
68 ran, so where stored credentials live was not established; `AMP_API_KEY` \
69 (strings: `documented AMP_API_KEY: present`) is the documented bypass variable",
70 },
71 CapabilityClaim {
72 capability: Capability::StateIsolation,
73 state: CapabilityState::NotGuaranteed,
74 basis: "measured: `+ f /home/agent/.local/share/amp/accounts.json` and \
75 `+ f .../device-id.json` were written at the default while pointed at the flag's \
76 target — the device identity and account store do not move with `--settings-file`",
77 },
78 ],
79 env: &[],
80 conflicts: &[],
84 credential_env: &["AMP_API_KEY"],
85};
86
87#[derive(Debug, Clone, Copy)]
89pub struct Amp;
90
91impl Adapter for Amp {
92 fn metadata(&self) -> &'static AdapterMetadata {
93 &METADATA
94 }
95
96 fn paths(&self, root: &AppRoot, profile: &ProfileName) -> Vec<(PathBuf, PathKind)> {
97 let dir = profile_dir(root, profile, METADATA.id);
98 let file = dir.join(FILE_NAME);
99 vec![(dir, PathKind::Dir), (file, PathKind::File { contents: INITIAL_CONFIG })]
100 }
101
102 fn plan(&self, ctx: &PlanContext<'_>) -> Result<PlannedLaunch> {
103 let mut planned = METADATA.mechanism.plan(self, ctx)?;
104 planned.notes.push(LAYERING_NOTE.to_owned());
105 Ok(planned)
106 }
107}