Skip to content
Merged
21 changes: 17 additions & 4 deletions app/src/components/meetings/__tests__/HistorySection.test.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -33,13 +33,26 @@ beforeEach(() => {

const NOW = Date.now();

// Anchor the day-grouped fixtures to noon UTC of their respective days. The
// history rail buckets by UTC calendar day (`utcDayKey`), so a `NOW - 1h`
// timestamp lands in *yesterday's* bucket when the suite runs just after
// 00:00 UTC — CI hit this and the "Today" group vanished. Noon UTC always
// shares its day's key regardless of wall-clock time, making grouping
// deterministic. (No future-filtering in `groupRecords`, so a noon-today
// timestamp that is technically ahead of `now` still groups as Today.)
const noonTodayUtc = (() => {
const d = new Date();
d.setUTCHours(12, 0, 0, 0);
return d.getTime();
})();

const todayCall: MeetCallRecord = {
request_id: 'req-today',
meet_url: 'https://meet.google.com/abc-def-ghi',
bot_display_name: 'OpenHuman',
owner_display_name: 'Alice',
started_at_ms: NOW - 3600000,
ended_at_ms: NOW - 3000000,
started_at_ms: noonTodayUtc,
ended_at_ms: noonTodayUtc + 600000,
listened_seconds: 300,
spoken_seconds: 60,
turn_count: 5,
Expand All @@ -51,8 +64,8 @@ const yesterdayCall: MeetCallRecord = {
meet_url: 'https://zoom.us/j/999888',
bot_display_name: 'OpenHuman',
owner_display_name: 'Bob',
started_at_ms: NOW - 86400000 - 3600000,
ended_at_ms: NOW - 86400000 - 3000000,
started_at_ms: noonTodayUtc - 86400000,
ended_at_ms: noonTodayUtc - 86400000 + 600000,
listened_seconds: 120,
spoken_seconds: 30,
turn_count: 2,
Expand Down
14 changes: 13 additions & 1 deletion app/src/components/meetings/__tests__/UpcomingTable.test.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -105,7 +105,19 @@ describe('UpcomingTable', () => {
});

it('shows a date-group separator (Today)', async () => {
listMock.mockResolvedValueOnce([makeMeeting()]);
// Anchor the meeting to noon *today* rather than `NOW + 1h`. The default
// `NOW + 1h` fixture rolls into tomorrow's date bucket when the suite runs
// within an hour of local midnight (CI hit this at 23:14 UTC), so the
// "Today" separator never rendered. Noon today always shares today's day
// key regardless of wall-clock time, making the grouping deterministic.
const noonToday = new Date();
noonToday.setHours(12, 0, 0, 0);
listMock.mockResolvedValueOnce([
makeMeeting({
start_time_ms: noonToday.getTime(),
end_time_ms: noonToday.getTime() + 30 * 60 * 1000,
}),
]);
renderWithProviders(<UpcomingTable />);
await waitFor(() => expect(screen.getByText(/today/i)).toBeInTheDocument());
});
Expand Down
11 changes: 6 additions & 5 deletions src/api/config.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1202,18 +1202,19 @@ mod tests {
}

// Our own hosted backend still passes through (is_openhuman short-circuit),
// and an UNKNOWN custom backend at a bare `/v1` keeps its pass-through so
// we don't reroute real self-hosted backends (the deliberate non-match
// documented on `looks_like_local_ai_endpoint`).
// but an UNKNOWN custom backend at a bare `/v1` base is now classified as
// an OpenAI-compatible inference base (#4153, Signal 2) and falls back so
// control-plane calls are not misrouted. A self-hosted backend must use a
// non-`/v1` base (see the `my-openhuman.example.com` case) to keep routing.
assert_eq!(
effective_backend_api_url(&Some("https://api.tinyhumans.ai/v1".to_string())),
"https://api.tinyhumans.ai",
"openhuman backend host must pass through"
);
assert_eq!(
effective_backend_api_url(&Some("https://my-backend.example/v1".to_string())),
"https://my-backend.example",
"unknown custom backend must keep pass-through"
fallback,
"unknown bare-/v1 base is an inference base and must fall back"
);
}

Expand Down
1 change: 0 additions & 1 deletion src/openhuman/memory_sync/sources/rebuild.rs
Original file line number Diff line number Diff line change
Expand Up @@ -272,7 +272,6 @@ pub async fn rebuild_tree_from_raw(
archive_source_id: &str,
) -> Result<RebuildOutcome> {
let start = std::time::Instant::now();
let content_root = config.memory_tree_content_root();

let coverage = raw_coverage(config, tree_scope, archive_source_id)?;
tracing::info!(
Expand Down
6 changes: 4 additions & 2 deletions src/openhuman/tinyagents/middleware.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1594,8 +1594,10 @@ impl Middleware<()> for CostBudgetMiddleware {
/// - [`NoProgress::Continue`] — do nothing.
/// - [`NoProgress::Nudge`] — inject the crate's structured "no progress since
/// step X" corrective into the working transcript via
/// [`SteeringCommand::Redirect`] so the next model call sees it and changes
/// strategy *before* the same-strategy retry cap trips.
/// [`SteeringCommand::InjectMessage`] so the next model call sees it and
/// changes strategy *before* the same-strategy retry cap trips. (Not
/// `Redirect`: that verb is outside the Interactive steering allowlist and
/// would abort the turn — see the nudge call site.)
/// - [`NoProgress::Halt`] — record the crate's root-cause summary into the shared
/// [`HaltSummarySlot`](super::HaltSummarySlot) (the turn overrides its final
/// text with it) and pause the run via the shared steering handle (same
Expand Down
2 changes: 1 addition & 1 deletion src/openhuman/tinyagents/tests.rs
Original file line number Diff line number Diff line change
Expand Up @@ -513,7 +513,7 @@ fn adapter_inventory_registers_model_tools_and_middleware() {
// context compression + message trim (window known + autocompact on), SDK
// tool-policy projection, tool-outcome capture, arg recovery.
let mw = assembled.harness.middleware();
assert_eq!(mw.len(), 14, "lifecycle middleware inventory");
assert_eq!(mw.len(), 13, "lifecycle middleware inventory");
// Around-tool wraps: approval/security + CLI/RPC-only scope gate (no
// builder tool policy on this call).
assert_eq!(mw.tool_middleware_len(), 2, "tool middleware inventory");
Expand Down
17 changes: 16 additions & 1 deletion tests/agent_session_turn_raw_coverage_e2e.rs
Original file line number Diff line number Diff line change
Expand Up @@ -787,8 +787,23 @@ async fn turn_xml_failures_checkpoint_policy_visibility_and_hooks_are_publicly_e
channel_permissions,
..AgentConfig::default()
})
// Budget must clear the rendered policy-denial message (~400 B) so the
// `denied by policy 'round17-deny'` assertion below still sees it. Before
// the tinyagents 1.5 migration (#4473) policy denials bypassed the
// per-result budget entirely; the migration now routes them through
// `ToolOutputMiddleware.after_tool`, so a tiny 96 B budget truncated the
// denial down to a `[… truncated …]` stub and the assertion no longer
// saw it. Production's default budget is 16 KiB, so real denials (~400 B)
// are never truncated — the old 96 B here was an artificial value with no
// assertion depending on truncation actually happening.
// TODO(follow-up): restore the "policy denials are exempt from the
// per-result budget" contract in production (tag the denial render with
// POLICY_BLOCKED_MARKER and skip the budget/persist path for it in
// `ToolOutputMiddleware.after_tool`). That also re-enables the no-progress
// `hard_reject` fast-path, which currently never fires for policy denials
// because their render omits the marker it greps for.
.context_config(ContextConfig {
tool_result_budget_bytes: 96,
tool_result_budget_bytes: 8192,
..ContextConfig::default()
})
.post_turn_hooks(vec![Arc::new(RecordingHook {
Expand Down
2 changes: 1 addition & 1 deletion tests/monitor_agent_e2e.rs
Original file line number Diff line number Diff line change
Expand Up @@ -498,7 +498,7 @@ async fn orchestrator_gets_denial_when_monitor_command_violates_policy() {
ProviderStep::FromHistory(Box::new(|messages| {
let text = all_messages_text(messages);
assert!(
text.contains("[policy-blocked] Security policy"),
text.contains("[policy-blocked] Tool 'monitor' was blocked by the security policy"),
"expected policy denial in messages:\n{text}"
);
assert!(
Expand Down
Loading