Skip to content

Commit 7ec0e02

Browse files
committed
v0.2.5 release
1 parent 7c81c18 commit 7ec0e02

File tree

12 files changed

+807
-45
lines changed

12 files changed

+807
-45
lines changed

Cargo.lock

Lines changed: 14 additions & 14 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

Cargo.toml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -18,7 +18,7 @@ members = [
1818
]
1919

2020
[workspace.package]
21-
version = "0.2.4"
21+
version = "0.2.5"
2222
edition = "2021"
2323
license = "Apache-2.0 OR MIT"
2424
repository = "https://github.com/RightNow-AI/openfang"

crates/openfang-api/src/routes.rs

Lines changed: 99 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -4352,6 +4352,55 @@ pub async fn agent_budget_ranking(State(state): State<Arc<AppState>>) -> impl In
43524352
Json(serde_json::json!({"agents": agents, "total": agents.len()}))
43534353
}
43544354

4355+
/// PUT /api/budget/agents/{id} — Update per-agent budget limits at runtime.
4356+
pub async fn update_agent_budget(
4357+
State(state): State<Arc<AppState>>,
4358+
Path(id): Path<String>,
4359+
Json(body): Json<serde_json::Value>,
4360+
) -> impl IntoResponse {
4361+
let agent_id: AgentId = match id.parse() {
4362+
Ok(id) => id,
4363+
Err(_) => {
4364+
return (
4365+
StatusCode::BAD_REQUEST,
4366+
Json(serde_json::json!({"error": "Invalid agent ID"})),
4367+
)
4368+
}
4369+
};
4370+
4371+
let hourly = body["max_cost_per_hour_usd"].as_f64();
4372+
let daily = body["max_cost_per_day_usd"].as_f64();
4373+
let monthly = body["max_cost_per_month_usd"].as_f64();
4374+
4375+
if hourly.is_none() && daily.is_none() && monthly.is_none() {
4376+
return (
4377+
StatusCode::BAD_REQUEST,
4378+
Json(serde_json::json!({"error": "Provide at least one of: max_cost_per_hour_usd, max_cost_per_day_usd, max_cost_per_month_usd"})),
4379+
);
4380+
}
4381+
4382+
match state
4383+
.kernel
4384+
.registry
4385+
.update_resources(agent_id, hourly, daily, monthly)
4386+
{
4387+
Ok(()) => {
4388+
// Persist updated entry
4389+
if let Some(entry) = state.kernel.registry.get(agent_id) {
4390+
let _ = state.kernel.memory.save_agent(&entry);
4391+
}
4392+
(
4393+
StatusCode::OK,
4394+
Json(serde_json::json!({"status": "ok", "message": "Agent budget updated"})),
4395+
)
4396+
}
4397+
Err(e) => (
4398+
StatusCode::NOT_FOUND,
4399+
Json(serde_json::json!({"error": format!("{e}")})),
4400+
),
4401+
}
4402+
}
4403+
43554404
// ---------------------------------------------------------------------------
43564405
// Session listing endpoints
43574406
// ---------------------------------------------------------------------------
@@ -5632,6 +5681,32 @@ pub async fn reset_session(
56325681
}
56335682
}
56345683

5684+
/// DELETE /api/agents/{id}/history — Clear ALL conversation history for an agent.
5685+
pub async fn clear_agent_history(
5686+
State(state): State<Arc<AppState>>,
5687+
Path(id): Path<String>,
5688+
) -> impl IntoResponse {
5689+
let agent_id: AgentId = match id.parse() {
5690+
Ok(id) => id,
5691+
Err(_) => {
5692+
return (
5693+
StatusCode::BAD_REQUEST,
5694+
Json(serde_json::json!({"error": "Invalid agent ID"})),
5695+
)
5696+
}
5697+
};
5698+
match state.kernel.clear_agent_history(agent_id) {
5699+
Ok(()) => (
5700+
StatusCode::OK,
5701+
Json(serde_json::json!({"status": "ok", "message": "All history cleared"})),
5702+
),
5703+
Err(e) => (
5704+
StatusCode::INTERNAL_SERVER_ERROR,
5705+
Json(serde_json::json!({"error": format!("{e}")})),
5706+
),
5707+
}
5708+
}
5709+
56355710
/// POST /api/agents/{id}/session/compact — Trigger LLM session compaction.
56365711
pub async fn compact_session(
56375712
State(state): State<Arc<AppState>>,
@@ -9253,39 +9328,46 @@ fn audit_to_comms_event(
92539328
};
92549329

92559330
let action_str = format!("{:?}", entry.action);
9256-
let (kind, detail) = match action_str.as_str() {
9257-
"AgentMessage" => (
9258-
CommsEventKind::AgentMessage,
9259-
openfang_types::truncate_str(&entry.detail, 200).to_string(),
9260-
),
9331+
let (kind, detail, target_label) = match action_str.as_str() {
9332+
"AgentMessage" => {
9333+
// Format detail: "tokens_in=X, tokens_out=Y" → readable summary
9334+
let detail = if entry.detail.starts_with("tokens_in=") {
9335+
let parts: Vec<&str> = entry.detail.split(", ").collect();
9336+
let in_tok = parts.first().and_then(|p| p.strip_prefix("tokens_in=")).unwrap_or("?");
9337+
let out_tok = parts.get(1).and_then(|p| p.strip_prefix("tokens_out=")).unwrap_or("?");
9338+
if entry.outcome == "ok" {
9339+
format!("{} in / {} out tokens", in_tok, out_tok)
9340+
} else {
9341+
format!("{} in / {} out — {}", in_tok, out_tok, openfang_types::truncate_str(&entry.outcome, 80))
9342+
}
9343+
} else if entry.outcome != "ok" {
9344+
format!("{} — {}", openfang_types::truncate_str(&entry.detail, 80), openfang_types::truncate_str(&entry.outcome, 80))
9345+
} else {
9346+
openfang_types::truncate_str(&entry.detail, 200).to_string()
9347+
};
9348+
(CommsEventKind::AgentMessage, detail, "user")
9349+
}
92619350
"AgentSpawn" => (
92629351
CommsEventKind::AgentSpawned,
92639352
format!("Agent spawned: {}", openfang_types::truncate_str(&entry.detail, 100)),
9353+
"",
92649354
),
92659355
"AgentKill" => (
92669356
CommsEventKind::AgentTerminated,
92679357
format!("Agent killed: {}", openfang_types::truncate_str(&entry.detail, 100)),
9358+
"",
92689359
),
9269-
"ToolInvoke" => return None,
9270-
"CapabilityCheck" => return None,
9271-
"MemoryAccess" => return None,
9272-
"FileAccess" => return None,
9273-
"NetworkAccess" => return None,
9274-
"ShellExec" => return None,
9275-
"AuthAttempt" => return None,
9276-
"WireConnect" => return None,
9277-
"ConfigChange" => return None,
92789360
_ => return None,
92799361
};
92809362

92819363
Some(CommsEvent {
9282-
id: entry.seq.to_string(),
9364+
id: format!("audit-{}", entry.seq),
92839365
timestamp: entry.timestamp.clone(),
92849366
kind,
92859367
source_id: entry.agent_id.clone(),
92869368
source_name: resolve_name(&entry.agent_id),
9287-
target_id: String::new(),
9288-
target_name: String::new(),
9369+
target_id: if target_label.is_empty() { String::new() } else { target_label.to_string() },
9370+
target_name: if target_label.is_empty() { String::new() } else { target_label.to_string() },
92899371
detail,
92909372
})
92919373
}

crates/openfang-api/src/server.rs

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -156,6 +156,10 @@ pub async fn build_router(
156156
"/api/agents/{id}/session/reset",
157157
axum::routing::post(routes::reset_session),
158158
)
159+
.route(
160+
"/api/agents/{id}/history",
161+
axum::routing::delete(routes::clear_agent_history),
162+
)
159163
.route(
160164
"/api/agents/{id}/session/compact",
161165
axum::routing::post(routes::compact_session),
@@ -447,7 +451,8 @@ pub async fn build_router(
447451
)
448452
.route(
449453
"/api/budget/agents/{id}",
450-
axum::routing::get(routes::agent_budget_status),
454+
axum::routing::get(routes::agent_budget_status)
455+
.put(routes::update_agent_budget),
451456
)
452457
// Session endpoints
453458
.route("/api/sessions", axum::routing::get(routes::list_sessions))

crates/openfang-api/static/index_body.html

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -569,7 +569,7 @@ <h3 style="color:var(--text-secondary);margin-bottom:4px">No Recent Activity</h3
569569
<svg width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><circle cx="11" cy="11" r="8"/><path d="m21 21-4.35-4.35"/></svg>
570570
</button>
571571
<button class="btn btn-ghost btn-sm" @click="$store.app.toggleFocusMode()" title="Ctrl+Shift+F">
572-
<svg width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><template x-if="!$store.app.focusMode"><g><path d="M8 3H5a2 2 0 0 0-2 2v3"/><path d="M21 8V5a2 2 0 0 0-2-2h-3"/><path d="M3 16v3a2 2 0 0 0 2 2h3"/><path d="M16 21h3a2 2 0 0 0 2-2v-3"/></g></template><template x-if="$store.app.focusMode"><g><path d="M8 3v3a2 2 0 0 1-2 2H3"/><path d="M21 8h-3a2 2 0 0 1-2-2V3"/><path d="M3 16h3a2 2 0 0 1 2 2v3"/><path d="M16 21v-3a2 2 0 0 1 2-2h3"/></g></template></svg>
572+
<svg width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><g x-show="!$store.app.focusMode"><path d="M8 3H5a2 2 0 0 0-2 2v3"/><path d="M21 8V5a2 2 0 0 0-2-2h-3"/><path d="M3 16v3a2 2 0 0 0 2 2h3"/><path d="M16 21h3a2 2 0 0 0 2-2v-3"/></g><g x-show="$store.app.focusMode"><path d="M8 3v3a2 2 0 0 1-2 2H3"/><path d="M21 8h-3a2 2 0 0 1-2-2V3"/><path d="M3 16h3a2 2 0 0 1 2 2v3"/><path d="M16 21v-3a2 2 0 0 1 2-2h3"/></g></svg>
573573
</button>
574574
<button class="btn btn-danger btn-sm" @click="killAgent()">Stop</button>
575575
</div>

0 commit comments

Comments
 (0)