auto(0129): agents_dashboard — secret_store_cpp_infra + CMakeLists register #4

Open
dataforge wants to merge 615 commits from auto/0129 into master
2 changed files with 173 additions and 0 deletions
Showing only changes of commit c2bdc586a4 - Show all commits
@@ -0,0 +1,110 @@
#include "viz/agent_runs_timeline_helpers.h"
#include <algorithm>
// Tabler icon macros live in core/icons_tabler.h. They are short (3-byte
// utf-8) string literals like "\xee\xa9\x9e". We don't depend on that header
// here directly to keep helpers buildable without the icon font tree — but
// the values below are pulled from cpp/functions/core/icons_tabler.h
// (Tabler v3.41). If a glyph moves upstream, update this table.
namespace fn_viz {
namespace timeline {
namespace {
// Mirror of icons_tabler.h entries used by the timeline. Kept inline so the
// helpers TU compiles standalone (tests don't pull the full icon font).
constexpr const char* TI_CLOCK_ = "\xee\xa9\xb0"; // U+EA70
constexpr const char* TI_LOADER_ = "\xee\xb2\xa3"; // U+ECA3 spinning loader
constexpr const char* TI_CIRCLE_CHECK_ = "\xee\xa9\xa7"; // U+EA67
constexpr const char* TI_CHECKS_ = "\xee\xae\xaa"; // U+EBAA
constexpr const char* TI_GIT_MERGE_ = "\xee\xaa\xb5"; // U+EAB5
constexpr const char* TI_BAN_ = "\xee\xa8\xae"; // U+EA2E
constexpr const char* TI_CIRCLE_X_ = "\xee\xa9\xaa"; // U+EA6A
constexpr const char* TI_HOURGLASS_ = "\xee\xbe\x93"; // U+EF93
bool contains(const std::vector<std::string>& v, const std::string& needle) {
for (const auto& s : v) {
if (s == needle) return true;
}
return false;
}
} // namespace
bool passes_filter(const AgentRun& r, const TimelineFilter& f) {
if (!f.apps.empty() && !contains(f.apps, r.app)) return false;
if (!f.statuses.empty() && !contains(f.statuses, r.status)) return false;
if (f.since_ts > 0 && r.started_at < f.since_ts) return false;
return true;
}
std::vector<AgentRun> filter_and_sort(const std::vector<AgentRun>& runs,
const TimelineFilter& f) {
std::vector<AgentRun> out;
out.reserve(runs.size());
for (const auto& r : runs) {
if (passes_filter(r, f)) out.push_back(r);
}
std::sort(out.begin(), out.end(), [](const AgentRun& a, const AgentRun& b) {
if (a.started_at != b.started_at) return a.started_at > b.started_at;
return a.id < b.id;
});
return out;
}
std::string format_duration(int64_t started_at, int64_t finished_at) {
if (finished_at == 0) return "running";
int64_t dur = finished_at - started_at;
if (dur < 0) return "";
char buf[32];
if (dur < 60) {
std::snprintf(buf, sizeof(buf), "%llds", (long long)dur);
} else if (dur < 3600) {
long long m = dur / 60;
long long s = dur % 60;
std::snprintf(buf, sizeof(buf), "%lldm%02llds", m, s);
} else {
long long h = dur / 3600;
long long m = (dur % 3600) / 60;
std::snprintf(buf, sizeof(buf), "%lldh%02lldm", h, m);
}
return std::string(buf);
}
int status_color_token(const std::string& status) {
if (status == "pending") return 1; // info
if (status == "running") return 1; // info
if (status == "done") return 2; // success
if (status == "validated") return 2; // success
if (status == "merged") return 3; // primary
if (status == "aborted") return 5; // warning
if (status == "failed") return 6; // danger
return 0; // neutral
}
std::string status_icon_id(const std::string& status) {
if (status == "pending") return TI_CLOCK_;
if (status == "running") return TI_LOADER_;
if (status == "done") return TI_CIRCLE_CHECK_;
if (status == "validated") return TI_CHECKS_;
if (status == "merged") return TI_GIT_MERGE_;
if (status == "aborted") return TI_BAN_;
if (status == "failed") return TI_CIRCLE_X_;
return TI_HOURGLASS_;
}
std::string app_chip_hex(const std::string& app) {
// Hardcoded palette. Keep aligned with the apps' icon.accent in app.md.
if (app == "kanban_cpp") return "#a855f7"; // violet
if (app == "skill_tree") return "#0ea5e9"; // sky
if (app == "graph_explorer") return "#16a34a"; // green
if (app == "shaders_lab") return "#f97316"; // orange
if (app == "registry_dashboard") return "#0ea5e9";
return "#5C5F66"; // neutral gray (matches fn_tokens::border_strong)
}
} // namespace timeline
} // namespace fn_viz
@@ -0,0 +1,63 @@
#pragma once
//
// agent_runs_timeline_helpers.h — pure helpers for the agent runs timeline panel.
//
// These helpers operate on AgentRun + TimelineFilter from agent_runs_timeline.h
// but never call ImGui. They are unit-testable in isolation (see
// cpp/tests/test_agent_runs_timeline.cpp).
//
// Issue 0118.
#include "viz/agent_runs_timeline.h"
#include <string>
#include <vector>
#include <cstdint>
namespace fn_viz {
namespace timeline {
// True iff `r` matches every active sub-filter:
// - filter.apps empty => any app
// - filter.statuses empty => any status
// - filter.since_ts == 0 => any started_at
// All filters are AND-combined.
bool passes_filter(const AgentRun& r, const TimelineFilter& f);
// Returns a copy of `runs` with `passes_filter` applied and sorted by
// `started_at` descending (most recent first). Stable for runs with equal
// started_at: ties broken by id ascending.
std::vector<AgentRun> filter_and_sort(const std::vector<AgentRun>& runs,
const TimelineFilter& f);
// Human-friendly duration string built from two unix-epoch SECOND timestamps.
// - finished_at == 0 => "running"
// - duration < 60s => "Ns" (e.g. "45s")
// - duration < 3600s => "MmSs" (e.g. "12m05s")
// - else => "HhMm" (e.g. "1h12m")
// Negative or inverted (finished < started) => "—".
std::string format_duration(int64_t started_at, int64_t finished_at);
// Returns a small integer color token (0..7) per status. The render layer
// maps this token to an actual ImVec4 using fn_tokens::colors.
//
// pending -> 1 (info)
// running -> 1 (info)
// done -> 2 (success)
// validated -> 2 (success)
// merged -> 3 (primary)
// aborted -> 5 (warning)
// failed -> 6 (danger)
// <other> -> 0 (neutral)
int status_color_token(const std::string& status);
// Returns the TI_* macro string for the given status (already utf-8 encoded
// 3-byte sequence). Render layer uses these directly in ImGui::Text.
std::string status_icon_id(const std::string& status);
// Hardcoded chip color hex for known app ids. Returns "#5C5F66" (border_strong
// / neutral gray) for unknown apps. Render layer parses the hex to ImVec4.
std::string app_chip_hex(const std::string& app);
} // namespace timeline
} // namespace fn_viz