agent surface: create_repo through hive-c0re (#1787)
Closes the #1787 loop — the sanctioned create path now that agents can't create repos directly. Adds: - wire: Request::CreateRepo{repo} + Response::RepoCreated{full_name, clone_url} (hive-sh4re). - agent_server: dispatch_shared arm + handle_create_repo — validates the repo name, then forge::create_agent_repo (org-owned repo, agent=write collaborator, operator-team branch protection). Returns the full name + clone url so the agent can git clone immediately. - MCP: create_repo tool + CreateRepoArgs in the harness. - a new opt-in ToolGroup::Forge (=[create_repo]) so the operator controls which agents can spin up repos (least privilege). Workspace clippy -D warnings, cargo test, nix fmt all green.
This commit is contained in:
parent
867be7bb98
commit
f1d54ce12c
4 changed files with 115 additions and 3 deletions
|
|
@ -59,6 +59,11 @@ pub enum SocketReply {
|
|||
hive_name: Option<String>,
|
||||
swarm_name: Option<String>,
|
||||
},
|
||||
/// `create_repo` result — the new repo's full name + clone URL.
|
||||
RepoCreated {
|
||||
full_name: String,
|
||||
clone_url: String,
|
||||
},
|
||||
}
|
||||
|
||||
impl From<hive_sh4re::Response> for SocketReply {
|
||||
|
|
@ -100,6 +105,13 @@ impl From<hive_sh4re::Response> for SocketReply {
|
|||
hive_name,
|
||||
swarm_name,
|
||||
},
|
||||
hive_sh4re::Response::RepoCreated {
|
||||
full_name,
|
||||
clone_url,
|
||||
} => Self::RepoCreated {
|
||||
full_name,
|
||||
clone_url,
|
||||
},
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -910,6 +922,35 @@ impl AgentServer {
|
|||
.await
|
||||
}
|
||||
|
||||
#[tool(
|
||||
description = "Create a git repo through hive-c0re. You CANNOT create repos with your \
|
||||
own forge token (creation is disabled) — this is the only path. The repo is created in \
|
||||
the c0re-owned `agents` org, you're added as a write collaborator (not owner), and the \
|
||||
default branch gets branch protection so merges require an operator-team approval — you \
|
||||
cannot merge your own PRs. `repo` is a single name segment (letters, digits, `-`, `_`, \
|
||||
`.`). Returns the new repo's full name + clone URL; clone it over \
|
||||
`http://localhost:3000/agents/<repo>.git` and push/open PRs as normal."
|
||||
)]
|
||||
async fn create_repo(&self, Parameters(args): Parameters<CreateRepoArgs>) -> String {
|
||||
let log = format!("{args:?}");
|
||||
run_tool_envelope("create_repo", log, async move {
|
||||
let (resp, retries) = self
|
||||
.dispatch(hive_sh4re::Request::CreateRepo { repo: args.repo })
|
||||
.await;
|
||||
let s = match resp {
|
||||
Ok(SocketReply::RepoCreated {
|
||||
full_name,
|
||||
clone_url,
|
||||
}) => format!("created repo {full_name} — clone: {clone_url}"),
|
||||
Ok(SocketReply::Err(m)) => format!("create_repo failed: {m}"),
|
||||
Ok(other) => format!("create_repo unexpected response: {other:?}"),
|
||||
Err(e) => format!("create_repo transport error: {e:#}"),
|
||||
};
|
||||
annotate_retries(s, retries)
|
||||
})
|
||||
.await
|
||||
}
|
||||
|
||||
#[tool(
|
||||
description = "Schedule a reminder that lands in this agent's own inbox at a future \
|
||||
time (sender will appear as `reminder`). Use for self-paced follow-ups: 'check task \
|
||||
|
|
@ -1505,6 +1546,13 @@ pub struct SetStatusArgs {
|
|||
pub text: String,
|
||||
}
|
||||
|
||||
#[derive(Debug, serde::Deserialize, schemars::JsonSchema)]
|
||||
pub struct CreateRepoArgs {
|
||||
/// Repo name — a single segment of letters, digits, `-`, `_`, `.`
|
||||
/// (no leading `-`/`.`). The repo is created as `agents/<repo>`.
|
||||
pub repo: String,
|
||||
}
|
||||
|
||||
#[derive(Debug, serde::Deserialize, schemars::JsonSchema)]
|
||||
pub struct GetAgentMetaArgs {
|
||||
/// Logical name of the agent to query (e.g. `"iris"`, `"manager"`).
|
||||
|
|
|
|||
Loading…
Reference in a new issue