Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
56 changes: 30 additions & 26 deletions Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 1 addition & 1 deletion Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ resolver = "2"


[workspace.package]
version = "0.1.5"
version = "0.2.11"
rust-version = "1.92"
edition = "2024"

Expand Down
18 changes: 17 additions & 1 deletion crates/forge_api/src/api.rs
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,8 @@ use anyhow::Result;
use forge_app::dto::ToolsOverview;
use forge_app::{User, UserUsage};
use forge_domain::{
AgentId, Effort, ModelId, ProviderModels, TrajectoryEvent, TrajectorySubscribeError, UserFact,
AgentId, Effort, ModelId, PendingNudge, ProviderModels, TrajectoryEvent,
TrajectorySubscribeError, UserFact,
};
use forge_stream::MpscStream;
use futures::stream::BoxStream;
Expand Down Expand Up @@ -122,6 +123,16 @@ pub trait API: Sync + Send {
/// deleted, `Ok(false)` when nothing matched (idempotent).
async fn delete_user_fact(&self, key: &str) -> Result<bool>;

/// Enqueues a pending nudge to be drained at the start of the next
/// user turn for `nudge.conversation_id`. Returns the assigned row
/// id. Issue #37.
async fn enqueue_nudge(&self, nudge: PendingNudge) -> Result<i32>;

/// Lists every nudge for `conversation_id`, consumed or not, in
/// reverse-chronological order. Used for inspection / debug — the
/// hot drain path is internal to the orchestrator (`next_unconsumed`).
async fn list_nudges(&self, conversation_id: &ConversationId) -> Result<Vec<PendingNudge>>;

/// Permanently deletes a conversation
///
/// # Arguments
Expand Down Expand Up @@ -253,6 +264,11 @@ pub trait API: Sync + Send {
/// Refresh MCP caches by fetching fresh data
async fn reload_mcp(&self) -> Result<()>;

/// Applies the interactive trust gate for any project-local MCP config.
/// Servers are NOT connected here — connections remain lazy and happen on
/// first tool use. Must be called once at startup.
async fn init_mcp(&self) -> Result<()>;

/// List of commands defined in .md file(s)
async fn get_commands(&self) -> Result<Vec<Command>>;

Expand Down
27 changes: 21 additions & 6 deletions crates/forge_api/src/forge_api.rs
Original file line number Diff line number Diff line change
Expand Up @@ -8,8 +8,8 @@ use forge_app::{
AgentProviderResolver, AgentRegistry, AppConfigService, AuthService, CommandInfra,
CommandLoaderService, ConversationService, DataGenerationApp, EnvironmentInfra,
FileDiscoveryService, ForgeApp, GitApp, GrpcInfra, McpConfigManager, McpService,
ProviderAuthService, ProviderService, Services, TrajectoryRepo, User, UserFactsRepo,
UserUsage, Walker, WorkspaceService,
NudgeRepo, ProviderAuthService, ProviderService, Services, TrajectoryRepo, User,
UserFactsRepo, UserUsage, Walker, WorkspaceService,
};
use forge_config::ForgeConfig;
use forge_domain::{Agent, ConsoleWriter, *};
Expand Down Expand Up @@ -80,6 +80,7 @@ impl<
+ GrpcInfra
+ TrajectoryRepo
+ UserFactsRepo
+ NudgeRepo
+ 'static,
> API for ForgeAPI<A, F>
{
Expand Down Expand Up @@ -242,10 +243,9 @@ impl<
) -> anyhow::Result<Vec<TrajectoryEvent>> {
// Walk every agent's events for this conversation in one query so
// /trace can render the parent → child agent tree without needing
// to know each child agent_id ahead of time.
self.infra
.list_for_conversation(&conversation_id.to_string())
.await
// to know each child agent_id ahead of time. Disambiguated against
// `NudgeRepo::list_for_conversation` which has the same signature.
TrajectoryRepo::list_for_conversation(&*self.infra, &conversation_id.to_string()).await
}

async fn subscribe_trajectory(
Expand Down Expand Up @@ -305,6 +305,17 @@ impl<
UserFactsRepo::delete(&*self.infra, key).await
}

async fn enqueue_nudge(&self, nudge: PendingNudge) -> anyhow::Result<i32> {
NudgeRepo::enqueue(&*self.infra, nudge).await
}

async fn list_nudges(
&self,
conversation_id: &ConversationId,
) -> anyhow::Result<Vec<PendingNudge>> {
NudgeRepo::list_for_conversation(&*self.infra, &conversation_id.to_string()).await
}

async fn delete_conversation(&self, conversation_id: &ConversationId) -> anyhow::Result<()> {
self.services.delete_conversation(conversation_id).await
}
Expand Down Expand Up @@ -427,6 +438,10 @@ impl<
async fn reload_mcp(&self) -> Result<()> {
self.services.mcp_service().reload_mcp().await
}

async fn init_mcp(&self) -> Result<()> {
self.services.mcp_service().init_mcp().await
}
async fn get_commands(&self) -> Result<Vec<Command>> {
self.services.get_commands().await
}
Expand Down
Loading