feat(kernel): host capability adapters, config seam, and the Phase 2 transcript soak - #5396
Conversation
tinyflows main moved 5 commits ahead of the pinned gitlink (declared workflow inputs, per-item fan-out, and the new memory + dedup node kinds). Three breakages fixed: - Capabilities gained a required memory field. Left None: a flow reaching user memory is policy-bearing (taint, source_scope, redaction on read; writes on remember/forget) and those guarantees live inside the memory domain today, so a raw adapter here would route flow traffic around them. Wiring waits on the kernel-side guard (docs/specs/kernel.md 3.4). - WorkflowGraph gained inputs; n8n import has no equivalent, so empty. - NodeKind match in flows::tools was non-exhaustive. Withhold memory and dedup from the advertised catalog rather than letting propose_workflow build graphs that cannot run here. dedup is the subtler one: the node only stages tentative keys and the crate leaves commit-on-success to the host, which OpenHuman does not yet do — so it would dedup within a run but never across runs, silently republishing. Absence beats a surface that returns a wrong answer. The count assertion in node_contracts is now derived from the crate catalog instead of hard-coded, so the next bump surfaces a new kind rather than failing on a stale 12. Also lands the Phase 0 trait-catalogue RFC gitlink for tinyagents and the kernel/agent-move specs. Co-authored-by: Medulla <medulla@tinyhumans.ai>
Host half of plan-agents Phase 3. session_config_from(&Config) plus apply_team_models / apply_delegate, following the tinycortex::config::memory_config_from precedent. Split three ways rather than one flat mapper because OpenHuman's model pins are not global: Config::teams is keyed by team and Config::agents by delegate, so 'the model for this session' is only knowable once you know which agent runs. A single function would have to invent it. Reads memory limits through resolved_memory_limits() rather than the legacy max_memory_context_chars scalar — that helper is what applies the memory_window preset and the hard ceiling, and bypassing it drops both. Nothing in agent/ is repointed yet; this is the foundation. Measured surface still to cover: 71 distinct config accesses across 37 production files. Co-authored-by: Medulla <medulla@tinyhumans.ai>
Phase 3 repointing. harness/required_output.rs is pure logic with no host domains, so it moves onto tinyagents' RequiredOutput outright; session_io's enforce_required_output follows, converting at the read site in turn/core.rs. The 12 existing tests pass unchanged, which is the proof the swap preserves behaviour. Splits the mapper into per-section functions (turn_config_from, tool_config_from, memory_limits_from, apply_agent_config). The session builder takes a per-agent AgentConfig override, so mapping only from the global Config would have discarded it and run every agent on the global limits. Records what the rest of Phase 3 actually requires. The '37 files' figure counts host-adapter files that should keep reading Config; the moving set is ~19 files / 41 refs, and 11 of those are Config::load_or_init() — ambient loads that need config threaded through signatures, not repointed. The session cannot drop AgentConfig until Phase 2 rehomes session_dual_write / session_shadow_reads, and a parallel crate config was rejected because runtime.rs mutates max_tool_iterations after build. Co-authored-by: Medulla <medulla@tinyhumans.ai>
Phase 3 ambient-load threading. Config::load_or_init() is NOT cached — it
re-resolves the config dirs and re-reads config.toml on every call — and
run_typed_mode called it six times, so a single sub-agent spawn hit the disk
six times and could observe six different configs if the file changed
mid-spawn. run_subagent now takes one snapshot and hands it down.
The snapshot is Result<Arc<Config>, String> rather than Option because the
integrations_agent path reports the load error to its caller while the other
five degrade silently; keeping both shapes lets every site preserve its
original failure behaviour exactly. It is taken after tier_gate_decision, since
load_or_init can initialize config on first run and a spawn the tier gate
rejects should not have that side effect.
The sub-agent graph gets the same treatment: build_subagent_context_mw takes
Option<&Config> (and is no longer async), plumbed through run_subagent_via_graph
and a new AgentTurnRequest::config field so the custom-graph path keeps its
[context] knobs instead of silently falling back to defaults. The four graph
tests now pass None, which makes them hermetic — they previously read whatever
config.toml happened to be on the developer's machine.
Scope correction: task_dispatcher/ is NOT in the moving set. plan-agents §3
lists it beside dispatcher.rs mapping to harness::{tool_calling, hooks}, but
that conflates two different modules. dispatcher.rs parses tool calls and is
generic; task_dispatcher/ is a task-CARD board dispatcher reaching task_sources,
threads, web_chat, todos, profiles and scheduler_gate. It stays host-side, so
its three load_or_init calls are correct as they are.
Co-authored-by: Medulla <medulla@tinyhumans.ai>
…tcher split Co-authored-by: Medulla <medulla@tinyhumans.ai>
Phase 1 landed in the crate; no OpenHuman code consumes them yet (that is Phase 4). Gitlink bump only. Co-authored-by: Medulla <medulla@tinyhumans.ai>
Phase 2 turned out to be mis-specified and mostly already built. The plan said 'transcript to crate JsonlChatHistory (transcript spec Option B)'. No such convergence is in progress. The real target, chosen and half-shipped under tinyhumansai#4249, is the crate's Store/AppendStore journal at {workspace}/tinyagents_store/. Building a JsonlChatHistory would have added a THIRD store beside the legacy JSONL and the one being migrated to. session_import/ (2452 LOC) already had the importer, the live dual-write (default ON), and the shadow-read comparison. Only the reader flip (04.2) is outstanding. Legacy session_raw/*.jsonl stays authoritative for both read and write; the store is mirror-only. That sequencing was already correct. Closed the two legacy-shape gaps this phase's own exit criteria name, neither of which had a test: - date-grouped session_raw/DDMMYYYY/ must resolve the same store stream as a flat transcript. The key is the file stem, so the enclosing directory must not change it — if it ever did, every pre-migration session would read as Unavailable and the soak would look clean while covering nothing. - a legacy .md session must read Unavailable, never Divergence. Those predate the store so no stream exists; reporting divergence would flood the soak with false positives from every old transcript on disk, and the point of a soak is that a warning means something. session_shadow_reads now defaults ON to start the soak. Observation-only: legacy stays authoritative, the probe runs on a background task once per resume rather than per turn, a store-read failure degrades to Unavailable, and OPENHUMAN_SESSION_SHADOW_READS=0 kills it. Worst case is log noise, not a broken resume. Deliberately NOT flipping readers. That is bought with soak evidence, and no evidence exists until a release ships with the probe on. Co-authored-by: Medulla <medulla@tinyhumans.ai>
Phase 4, first half. src/openhuman/tinyagents/host/ implements each crate host trait against the real OpenHuman domains (~6000 LOC, 140 tests). This is where policy lives: taint, scope, redaction, approval and egress budget are enforced on the way in and out of the trait, because the crate deliberately knows nothing about them. agent/ does NOT call these yet, so the exit criterion is unmoved at 295 prod refs in session/. Writing the adapters and repointing the callers are separate pieces of work; only the first is done. Two defects fixed at integration, both compiling cleanly and failing silently: - security_gate: a channel RequireApproval verdict resolved to Allow. The mapping returned 'no verdict' assuming the call would fall through to the approval park, but the park is only reached from the shell and external-effect branches — so any ordinary tool was authorized with nobody asked. agent_tool_policy::engine files RequireApproval under blocked_tool_names alongside Deny, so this inverted the host's own semantics. Latent only because build_session does not currently emit RequireApproval, i.e. a trap armed for whoever turns it on. Now parks, and denies when no gate exists — the one place this set denies where the legacy middleware allows, argued in the module header. - experience_store: stable_experience_id_for_profile hashes task + tool sequence + outcome + profile and excludes agent_id. The native capture hook is protected only incidentally by always supplying a real tool sequence; this adapter has none, so two agents recording the same task with the same outcome collided on one id and put upserted, the second writer destroying the first's record. Agent id now folded into the hashed tool-sequence slot. Also corrected the plan's exit-criterion baseline: it read ~2000 refs, measured 295 in session/ production code. 21 TODO(phase4) markers remain, each naming an unreachable domain surface — honest gaps rather than stubs pretending to work. Co-authored-by: Medulla <medulla@tinyhumans.ai>
86 upstream commits, including a large module reorganization. Resolutions: Flows / tinyflows — took upstream wholesale. Upstream independently did the same tinyflows bump I had done and then went further: it wires the memory capability for real (caps/ops.rs `memory: Some(..)`) and adds the dedup commit-on-success subscriber in flows/bus.rs. My HOST_UNSUPPORTED_NODE_KINDS gating was a correct interim measure for code where neither was wired; keeping it now would hide working functionality, so it is gone and all 14 node kinds are advertised. tinyflows/caps.rs was also split into flows/tinyflows/caps/* upstream, superseding my one-line edit there. vendor/tinyflows — took upstream's 548dbe38 over my a918a7a. Same feature line: "declared workflow inputs" was re-landed unsquashed as five commits plus one follow-up, so upstream's pin strictly contains mine. I only ever moved that gitlink, never authored in it, so nothing of mine is lost. vendor/tinyagents — genuine merge, both sides had work. Mine sat on 4358efe (RFC, config structs, all_keys, the ten host traits); upstream had advanced 16 commits (providers, embeddings, todos, message, harness fixes). Merged cleanly because my work is in new modules. 1356 crate tests pass. Module reorganization — my Phase 4 adapters moved with the tree to agent/tinyagents/host/ and their imports were remapped: agent_memory -> memory::agent, memory_store -> memory::store, agent_experience -> agent::experience, agent_registry -> agent::registry, agent_tool_policy -> tools::agent_policy, tool_status -> tools::status, cost -> platform::cost, scheduler_gate -> cron::scheduler_gate, tokenjuice -> inference::tokenjuice, profiles/context/learning -> agent::*, composio -> integrations::composio, approval/prompt_injection -> security::*, app_state -> desktop::app_state. Kept mine, rebased onto upstream's paths: the AgentTurnRequest::config field, the spawn-wide config snapshot threading in the sub-agent runner, and the context-middleware config parameter. Upstream had reintroduced Config::load_or_init() at two of those sites via its own edits; the threaded snapshot is retained. One test expectation updated: secrets_are_redacted_before_they_reach_the_store asserted the lowercase "[redacted]" marker. Upstream's stronger memory-store scrubber replaces the whole key=value with "[REDACTED]", so the assertion is now case-insensitive. The security property it guards — the secret must not survive — was passing throughout and is unchanged. Also initialized the new vendor/tinyhumans-sdk submodule. Agent suite: 2363 pass / 0 fail (the previously flaky profile_allowed_tools_restrict_shared_session_builder is fixed upstream). Flows 832, adapters 142, session_import 25. Co-authored-by: Medulla <medulla@tinyhumans.ai>
…bilities Phase 4 second half. Investigated the repointing and found it blocked rather than merely hard; landed the one piece that is not. Four findings, each measured, recorded in docs/specs/plan-agents.md: 1. The exit criterion counts the wrong code. Of 118 non-adapter refs in session/, 46 are in builder/ (assembly, which becomes the trait impls), 16 are field type annotations, 13 are state management. Only turn/'s ~40 is genuine runtime consumption, so "drive session/ down to the adapter layer" cannot happen by repointing — most of those refs ARE that layer. 2. The session distributes handles more than it consumes them. All 11 uses of self.memory hand the Arc<dyn Memory> to a collaborator that needs the full domain interface, so swapping the field to Arc<dyn AgentMemory> breaks them. 3. No capability trait fits an existing call shape 1:1. ContextComposer returns String where the turn needs structured LearnedContextData for its own SystemPromptBuilder; ToolOutcomeClassifier's only consumer is progress_tracing/, which the plan deletes rather than moves. 4. Four adapters (BudgetGate, ContextComposer, ModelResolver, and the policy half of SecurityGate) need Arc<Config>, which the session did not hold — it holds AgentConfig plus an optional, misleadingly-named integration_runtime_config. That is the Phase 3 blocker, which is blocked on Phase 2's reader flip, which is blocked on the parity soak needing a shipped release. The program is now gated on elapsed time, not effort. Unblocked and done here: integration_runtime_config becomes runtime_config: Option<Arc<Config>> — a first-class shared handle rather than an integrations-specific one — so those four adapters become constructible without waiting on Phase 2. Adds runtime_config() and host_capabilities_available(), plus host_agent_memory() and host_experience_store() for the two adapters that need only Arc<dyn Memory> and therefore work on the bare-builder path too. host_capabilities_available() keeps "this session cannot answer that" distinct from "the capability failed" — the same absence-versus-failure rule the traits are built on. Adapters are built on demand rather than stored: a stored handle could drift from self.memory if the backend were swapped. Full suite 12770 pass / 0 fail. Co-authored-by: Medulla <medulla@tinyhumans.ai>
tinyplace — FIXED, root cause understood. signal_register_encryption_key_fails_at_store_not_params asserted "does not fail on missing params" by calling unwrap_err(), which assumes the call ALWAYS fails. That holds only while the process-global signal store is uninitialized; once any other test in the binary initializes it the call succeeds and unwrap_err() panics. The property under test is about param validation, which an Ok satisfies as well as a non-param error, so the assertion now only runs when there is an error. Two siblings carried the same latent bug and are fixed too — signal_register_encryption_key_does_not_leak_key_material calls the very same handler, and directory_find_by_encryption_key_with_valid_param_fails_at_client has the identical shape against the directory handler. archivist — NOT FIXED. Two real isolation defects corrected, but the flake remains, so this is hardening rather than a fix. Both defects are genuine: these tests document themselves as hermetic and are not. 1. test_config_with_tree() claimed to "disable embedding so ingest doesn't fail trying to contact Ollama" but set memory_tree.embedding_*, while the tree ingest reads memory.embedding_model via memory::tinycortex::config::memory_config_from — which defaults to the CLOUD model "embedding-v1". So ingest really was calling the managed embedding service. Now sets embeddings_provider = "none", which memory::tree_e2e_tests::pipeline_works_with_embeddings_disabled pins as the route to InertEmbedder. 2. ingest_chat builds its OWN chat provider from Config (tinycortex::ingest::context -> scoring_config -> build_chat_provider), so it ignored the StubChatProvider wired into the hook. The five tree-ingest tests now run inside chat::test_override::with_provider, a task-local hook that build_chat_runtime already checks and that nothing was using. What was ruled out for the archivist flake, so the next attempt does not repeat it: instrumenting tree_ingest.rs with panics showed that on failing runs the ingest never returns Err, never sees empty messages, and never reports chunks_written == 0. The tests fail with "got 0 chunks" while none of those probes fire, which points at pipe_segment_to_tree not being reached at all — i.e. the segment boundary not firing — rather than at the ingest itself. Separately, the suite has a broader population of load-sensitive tests (memory::chat, memory::ops::documents, subconscious::monitors) that fail intermittently. An A/B with these changes stashed failed too, so that population is pre-existing and not caused here. Co-authored-by: Medulla <medulla@tinyhumans.ai>
…aits Rebased onto tinyagents main so the PR carried only this work — the previous pin sat on an unsquashed copy of the provider/embeddings chain that main has since landed squashed (tinyhumansai#81, tinyhumansai#85, tinyhumansai#86). tinyhumansai/tinyagents#87 is merged; this pins main (3e1dbea). Co-authored-by: Medulla <medulla@tinyhumans.ai>
Four needless borrows created by the spawn-wide config snapshot (the bindings are now &Arc<Config>, so &config re-borrows), and a manual case-insensitive compare in the experience adapter. Co-authored-by: Medulla <medulla@tinyhumans.ai>
There was a problem hiding this comment.
senamakel has reached the 50-credit limit for trial accounts. To continue receiving code reviews, upgrade your plan.
|
Warning Review limit reachedYou’ve reached a temporary PR review limit under our Fair Usage Limits Policy. Next review available in: 23 minutes Enable usage-based reviews in Billing to review now. Otherwise, wait until the next included review is available. How can I continue?After more reviews become available, a review can be triggered using the To avoid repeated limits, reduce automatic review volume by pausing incremental auto-reviews earlier, using label-based review opt-in, excluding WIP or generated PR titles, or requesting reviews manually when the PR is ready. If your team needs uninterrupted high-volume reviews, an organization admin can enable usage-based reviews. How do review limits work?CodeRabbit enforces per-developer PR review limits for each organization. Most developers receive the normal plan review availability. For paid Pro and Pro+ PR reviews, CodeRabbit uses adaptive limits for sustained high-volume activity. When a developer's recent PR review activity reaches the 95th percentile or higher among CodeRabbit users, additional reviews become available more gradually as earlier reviews age out of the rolling window. Please refer docs for additional details. Review details⚙️ Run configurationConfiguration used: Organization UI Review profile: CHILL Plan: Pro Run ID: 📒 Files selected for processing (14)
📝 WalkthroughWalkthroughThis PR adds four design specification documents for TinyAgents migration planning, implements ten TinyAgents host capability adapters (memory, budget gate, context composer, definition registry, experience store, learning sink, model resolver, progress sink, security gate, tool outcome classifier), a configuration-mapping module, renames session config storage to a shared ChangesArchitecture Design Specifications
Estimated code review effort: 3 (Moderate) | ~25 minutes TinyAgents Host Adapter Implementation
Estimated code review effort: 4 (Complex) | ~75 minutes Sequence Diagram(s)sequenceDiagram
participant Session as Agent Session
participant Resolver as OpenHumanModelResolver
participant Memory as OpenHumanAgentMemory
participant SecurityGate as OpenHumanSecurityGate
participant ProgressSink as OpenHumanProgressSink
Session->>Resolver: resolve(ModelResolveRequest)
Resolver-->>Session: ChatModel via StatelessModel
Session->>Memory: recall(namespace, limit)
Memory-->>Session: sanitized MemoryEntry list
Session->>SecurityGate: authorize_tool(ToolCallRequest)
SecurityGate-->>Session: GateDecision
Session->>ProgressSink: emit(ProgressEvent)
Possibly related issues
Possibly related PRs
Suggested labels: Suggested reviewers: Poem
🚥 Pre-merge checks | ✅ 5✅ Passed checks (5 passed)
Comment |
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: c7688e6f66
ℹ️ About Codex in GitHub
Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you
- Open a pull request for review
- Mark a draft as ready
- Comment "@codex review".
If Codex has suggestions, it will comment; otherwise it will react with 👍.
Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".
api.md was a byte-identical copy of plan-memory.md; delete it and retarget the three cross-links that still named the pre-rename filenames. The tinyagents RFC link becomes a URL because the link-check lane checks out without submodules, so a relative path into vendor/ can never resolve there.
There was a problem hiding this comment.
senamakel has reached the 50-credit limit for trial accounts. To continue receiving code reviews, upgrade your plan.
…retrieval The experience store now imports `retrieve_across_stores` from the experience store module, enabling the host to query experiences across multiple stores rather than being limited to a single store. This change prepares the codebase for broader experience retrieval capabilities. Co-authored-by: Medulla <medulla@tinyhumans.ai>
The experience store now consults an additional read-only store during recall, matching the live turn path that queries both profile-local and shared workspace stores. This ensures dedicated-profile sessions can still recall records written before profiles existed, while writes remain confined to the profile-local store. Co-authored-by: Medulla <medulla@tinyhumans.ai>
Adds an optional second store that recall reads from but record never writes to, enabling a shared pre-profile workspace store behind dedicated-profile sessions. The new builder method accepts None as a no-op, preserving single-store behaviour for profile-less sessions. Co-authored-by: Medulla <medulla@tinyhumans.ai>
…tore.rs Checkpoint of work in progress, touching src/openhuman/agent/tinyagents/host/experience_store.rs. Co-authored-by: Medulla <medulla@tinyhumans.ai>
…urn path The host experience store now writes exclusively to the session's own memory while recall additionally consults the shared experience memory when provided. This mirrors the behavior in the live turn path, ensuring new records stay within the profile-local subtree while old unstamped records from pre-profile builds remain reachable through shared recall. Co-authored-by: Medulla <medulla@tinyhumans.ai>
Adds a new migration that enables shadow reads for session data, allowing reads to be served from a secondary replica while writes continue to the primary. This improves read scalability without changing the application's write path. Co-authored-by: Medulla <medulla@tinyhumans.ai>
Adds the new enable_session_shadow_reads migration module to the migrations registry so it is included in the migration set. Co-authored-by: Medulla <medulla@tinyhumans.ai>
The schema version constant is incremented to reflect the addition of a new migration, ensuring the migration runner processes the newly added migration step. Co-authored-by: Medulla <medulla@tinyhumans.ai>
…-read parity soak Adds a schema version 8 to 9 migration that enables `agent.session_shadow_reads` for existing workspaces, since the serde default flipped to `true` but saved configs still carry an explicit `false`. The migration guards on `== 8` to avoid skipping an earlier failed step, and rolls back both the version and the flag if saving fails so the next launch retries cleanly. Co-authored-by: Medulla <medulla@tinyhumans.ai>
Replace hardcoded schema version 8 in migration tests with the CURRENT_SCHEMA_VERSION constant so assertions stay correct as the schema evolves. Co-authored-by: Medulla <medulla@tinyhumans.ai>
The store now pulls up to five times the configured max hits from the domain before applying the agent filter, so a shared store with several active agents no longer loses quieter agents' experiences. The multiplier is a heuristic rather than a guarantee, and a max_hits of zero still returns nothing to preserve the existing feed-none-back behaviour. Co-authored-by: Medulla <medulla@tinyhumans.ai>
Adds a regression test ensuring that a busy agent's records cannot crowd out another agent's matching attempts when truncation happens before the agent filter. The test verifies that recall still returns the correct agent's attempts and respects the max_hits bound. Co-authored-by: Medulla <medulla@tinyhumans.ai>
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: 409d11b1f2
ℹ️ About Codex in GitHub
Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you
- Open a pull request for review
- Mark a draft as ready
- Comment "@codex review".
If Codex has suggestions, it will comment; otherwise it will react with 👍.
Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".
Reformatted the test code in the experience store to improve readability by wrapping long function calls and method chains across multiple lines. No behavioral changes were made. Co-authored-by: Medulla <medulla@tinyhumans.ai>
The module documentation now explains that the ownership filter must run before truncation to `max_hits`, since the domain truncates before the adapter sees the rows. This ensures the limit applies to the current agent's attempts rather than a mixed page. Co-authored-by: Medulla <medulla@tinyhumans.ai>
…mposer The context composer now respects the `include_profile` and `include_memory_md` flags supplied through the `with_omissions` wiring, instead of hardcoding both to true. This aligns the seam with the subagent runner's behavior, which derives these from the resolved agent definition's omit flags, allowing per-agent customization of user file inclusion. Co-authored-by: Medulla <medulla@tinyhumans.ai>
The context composer now accepts explicit flags for whether the user's PROFILE.md and MEMORY.md layers are injected, defaulting to including both. This lets the wiring site apply a specialist definition's omit_profile and omit_memory_md settings, matching the live subagent runner behavior instead of always composing with the main agent's files. Co-authored-by: Medulla <medulla@tinyhumans.ai>
Adds a regression test verifying that `omit_profile` and `omit_memory_md` definitions are honoured when composing system prompts, ensuring opted-out files are not injected into the context. Co-authored-by: Medulla <medulla@tinyhumans.ai>
There was a problem hiding this comment.
senamakel has reached the 50-credit limit for trial accounts. To continue receiving code reviews, upgrade your plan.
Replace the single shared counter with per-run state keyed by run ID, so concurrent sub-runs no longer corrupt the parent's iteration count or prematurely complete the top-level turn. The first observed run is treated as the root, and only its lifecycle drives the turn-level events. Co-authored-by: Medulla <medulla@tinyhumans.ai>
The progress sink now maintains per-run state instead of a single global counter, so concurrent runs each get their own iteration count. Tool calls emitted in a parallel batch are grouped into one iteration, preventing overcounting when a model requests multiple tools at once. Co-authored-by: Medulla <medulla@tinyhumans.ai>
The progress sink previously used a single atomic counter for all runs, which caused incorrect iteration counts and premature turn completion when sub-runs emitted events. It now maintains per-run state in a map, tracking the root run separately so that only the top-level run triggers TurnStarted and TurnCompleted lifecycle events. Co-authored-by: Medulla <medulla@tinyhumans.ai>
Added a RunState struct to track model iteration rounds and tool-call batching, enabling the progress sink to distinguish between consecutive tool calls within a single model response and separate iterations. Co-authored-by: Medulla <medulla@tinyhumans.ai>
The module documentation now explains that iteration counts are derived per run, with a batch of consecutive tool calls forming one iteration and model output closing the batch. It also documents that a single sink may serve multiple concurrent runs, with counters keyed by run ID, and clarifies why no tool completion events are emitted. Co-authored-by: Medulla <medulla@tinyhumans.ai>
The documentation comment contained a corrupted phrase where "每 call" appeared instead of "every call". This fixes the typo to restore clarity in the explanation of iteration counting behavior. Co-authored-by: Medulla <medulla@tinyhumans.ai>
Reformats two chained method calls in the progress sink to use multi-line formatting for improved readability, with no behavioral changes. Co-authored-by: Medulla <medulla@tinyhumans.ai>
The round counter was incremented per tool call, so two tools requested in a single model response were reported as separate iterations. Now all tool calls within one LLM iteration share the same iteration number, and a new iteration only begins after model output. Co-authored-by: Medulla <medulla@tinyhumans.ai>
The comment in the test was updated to use English instead of Russian, clarifying the iteration numbering for the model's reply. Co-authored-by: Medulla <medulla@tinyhumans.ai>
Add tests ensuring a child run's finish does not surface as the request's completion and that child tool calls do not advance the parent's iteration counter, preserving correct turn and iteration semantics on shared sinks. Co-authored-by: Medulla <medulla@tinyhumans.ai>
There was a problem hiding this comment.
senamakel has reached the 50-credit limit for trial accounts. To continue receiving code reviews, upgrade your plan.
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: 7bf24506e3
ℹ️ About Codex in GitHub
Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you
- Open a pull request for review
- Mark a draft as ready
- Comment "@codex review".
If Codex has suggestions, it will comment; otherwise it will react with 👍.
Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".
Summary
agent/tinyagents/host/) wiring OpenHuman's domains to the TinyAgents traits landed in feat(harness): host capability traits + crate-owned session config tinyagents#87. Nothing calls them yet — this is the seam, not the switchover.tinyagents::harness::configstructs plus a host mapper, so the agent runtime can stop readingConfigdirectly.Config::load_or_init()is not cached, andrun_typed_modewas calling it six times per spawn.session_shadow_readsnow defaults ON) and closes the two legacy-shape coverage gaps its exit criteria name.Problem
src/openhuman/agent/reaches 45 domains, which blocks making the agent runtime generic (docs/specs/plan-agents.md). The runtime also loaded host config ambiently and in places bypassed guards it should have asked about.Two latent defects surfaced while building the adapters, both of which compile cleanly and fail silently:
RequireApprovalverdict resolved toAllow. The mapping returned "no verdict" assuming the call would fall through to the approval park — but the park is only reached from theshelland external-effect branches, so any ordinary tool was authorized with nobody asked.tools::agent_policy::enginefilesRequireApprovalunderblocked_tool_namesalongsideDeny, so this inverted the host's own semantics. Latent only becausebuild_sessiondoes not currently emit it.stable_experience_id_for_profilehashes task + tool sequence + outcome + profile and deliberately excludesagent_id; the native capture hook is protected only incidentally by always supplying a real tool sequence. This adapter has none, so two agents recording the same task with the same outcome collided on one id andputupserted — the second writer silently destroying the first's record.Solution
Adapters live in
agent/tinyagents/host/, one per capability, with policy enforced adapter-side (taint, scope, redaction, approval, budget) because the crate deliberately knows nothing about it. Optionality is modelled as absence, never an erroring default — a stub that errors teaches the loop the capability exists and invites a retry.RequireApprovalnow routes to the approval park, and denies when no gate is installed. That is the one place this set is stricter than the legacy middleware; the reasoning is argued in the module header rather than left as a surprise. The experience id now folds the agent into the hashed tool-sequence slot.The sub-agent runner takes one config snapshot per spawn (
Result<Arc<Config>, String>, so each site keeps its original failure behaviour) aftertier_gate_decision, sinceload_or_initcan initialize config on first run and a rejected spawn should not have that side effect.session_shadow_readsdefaults ON to start the parity soak. It is observation-only: legacy stays authoritative, the probe runs on a background task once per resume, a store-read failure degrades toUnavailable, andOPENHUMAN_SESSION_SHADOW_READS=0kills it.Submission Checklist
require_approval_never_silently_allows_a_plain_tool) and the two legacy transcript shapes (DDMMYYYY/and legacy.md)Rust Core Coverage (cargo-llvm-cov)+PR CI Gate) rather than measured locally.N/A: no user-facing feature rows added or removed; this is an internal seam plus a default-flag flipN/A: no matrix rows touchedN/A: no release-cut surface touchedN/A: tracked by docs/specs/plan-agents.md, no GitHub issueImpact
Behaviour change:
session_shadow_readsdefaults ON. Observation-only and kill-switchable, per above.Security: the
RequireApprovalfix is a tightening. If anything begins emittingRequireApproval, tools that previously ran unasked will now prompt, and deny where no approval flow exists.Desktop/CLI/headless all affected equally (core-side). No migration, no schema change. The ten adapters are additive and currently uncalled, so runtime behaviour is unchanged by them.
Follow-up, recorded in
docs/specs/plan-agents.md: repointingagent/to call these adapters is blocked — four of them needArc<Config>, which is the Phase 3 blocker, which waits on Phase 2's reader flip, which needs soak data from a shipped release. The plan's Phase 4 exit criterion was also corrected: it read "~2,000 refs" where the measured figure is 295, two-thirds of which is assembly code that becomes the trait impls rather than disappearing.Related
Depends on tinyhumansai/tinyagents#87 (merged; this PR pins tinyagents
main).Note for reviewers on the test suite
cargo testneeds real temp space. This repo's suite plus rustdoc fill a smalltmpfs/tmp, and the failures that produces look like unrelated flaky tests (archivist,tinyplace,memory::*,subconscious::monitors) rather than a full disk. WithTMPDIRon a real filesystem the suite is 12770 pass / 0 fail across repeated runs.Summary by CodeRabbit
New Features
Documentation
Tests