Skip to content

feat: time awareness subsystem for continuous temporal perception - #61837

Open
k-QRedHacker wants to merge 1 commit into
NousResearch:mainfrom
k-QRedHacker:feat/time-awareness-clean
Open

feat: time awareness subsystem for continuous temporal perception#61837
k-QRedHacker wants to merge 1 commit into
NousResearch:mainfrom
k-QRedHacker:feat/time-awareness-clean

Conversation

@k-QRedHacker

Copy link
Copy Markdown

Time Awareness: Three-Layer Temporal Perception

This PR implements a Time Awareness subsystem for Hermes Agent — giving the agent continuous perception of time flow across sessions and turns.

What This Does

Layer 1 — Dormancy Perception (system prompt, cache-safe)

  • At session build time, reads ~/.hermes/time_state.json for the last sleep timestamp
  • Computes dormancy duration and injects it into volatile_parts
  • Agent learns "how long I slept" on every wake
  • Cache safety: computed once at build_system_prompt_parts() time, byte-stable for the session lifetime

Layer 2 — Heartbeat Rhythm (per-turn messages, not system prompt)

  • Before each API call, injects a compact heartbeat into api_messages
  • Throttled to once per 5 minutes to avoid token waste
  • Agent learns "what time it is now" and "how long since I last spoke"
  • Cache safety: injected into the per-turn message list, NOT the system prompt. The system prompt prefix cache is untouched.

Layer 3 — Lifecycle Anchoring (session transitions)

  • On every session transition (/new, /reset, gateway expiry, agent close), writes the sleep timestamp
  • Closes the loop so Layer 1 can compute dormancy on next wake

Files Changed

File Change Lines
agent/time_awareness.py New module — standalone time awareness subsystem +205
agent/system_prompt.py Layer 1 injection in build_system_prompt_parts() +18
agent/chat_completion_helpers.py Layer 2 injection in build_api_kwargs() +23
run_agent.py Layer 3 call in _transition_context_engine_session() +8

Total: 4 files, 254 insertions.

Cache Safety

This PR does NOT break prompt caching:

  1. Layer 1 is computed once at session-build time and appended to volatile_parts. The volatile_parts tier is rebuilt only at session boundary / compression events — not per-turn. The result is byte-stable for the session.
  2. Layer 2 is injected into api_messages (the per-turn message array), NOT the system prompt. Prefix-cached system prompt bytes are untouched.
  3. The 5-minute throttle on Layer 2 means typical sessions see only 1-3 heartbeat injections.

Design Decisions

  • Atomic writes via os.replace(tmp, path) prevents state file corruption on crash
  • Graceful degradation: all calls wrapped in try/except — failure silently passes, never blocks prompt build or API calls
  • No external dependencies beyond stdlib
  • Profile-safe: uses get_hermes_home() for path resolution

Original

This supersedes #61731 and #61738 (which were duplicates and bundled unrelated changes). This PR contains only the time awareness subsystem.

Three-layer implementation gives the agent continuous perception of time
flow across sessions and turns without breaking prompt caching:

- Layer 1 (Dormancy Perception): Injected into system prompt volatile_parts
  at session-build time. Agent learns how long it was dormant between sessions.
  Cache-safe: computed once at build time, byte-stable for session lifetime.

- Layer 2 (Heartbeat Rhythm): Injected into per-turn api_messages before each
  API call, throttled to once per 5 minutes. Agent learns current time and
  inter-turn intervals. Cache-safe: touches api_messages, not system prompt.

- Layer 3 (Lifecycle Anchoring): Writes sleep timestamp on every session
  transition (/new, /reset, gateway expiry, agent close).

State file: ~/.hermes/time_state.json (atomic writes, graceful degradation)

Co-Authored-By: Claw F (量子红客组织)
@alt-glitch alt-glitch added type/feature New feature or request P3 Low — cosmetic, nice to have comp/agent Core agent runtime: loop, agent_init, prompt builder, context-compression, responses endpoint sweeper:risk-caching Sweeper risk: may break/degrade prompt caching or cache-key stability (invariant) duplicate This issue or pull request already exists labels Jul 10, 2026
@alt-glitch

Copy link
Copy Markdown
Collaborator

This was generated by AI during triage.

Duplicate of #61731 -- same author, same title, and byte-identical 4-file set (agent/time_awareness.py, agent/system_prompt.py, agent/chat_completion_helpers.py, run_agent.py). #61731 is the earliest-open canonical of this Time Awareness cluster (the intermediate #61738 was already closed). Consolidating here; please continue on #61731.

@k-QRedHacker

Copy link
Copy Markdown
Author

Quick clarification on the duplicate flag: #61731 was closed by us during the PR split — it bundled time-awareness core + session handoff together. #61837 is the clean, standalone time-awareness PR (4 files, 254 lines). #61731 and #61837 share the same commit history up to the split point, which is why the file sets look identical. #61837 is the current version. Thanks for the triage.

@teknium1 teknium1 left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks for separating this from the earlier bundled PR. This needs lifecycle and state-ownership rework before it can provide the advertised guarantees.

Problems

  • Proposed run_agent.py:650 sits after the compressor guard: current run_agent.py:635-637 returns when no context compressor exists, so Layer 3 does not run in that supported configuration. AIAgent.close() (run_agent.py:3463) also does not call this transition helper, despite the PR claiming agent-close coverage.
  • agent/time_awareness.py:43 uses one profile-wide state file. on_api_call() reads and overwrites the single heartbeat timestamp at lines 160-182, so concurrent sessions affect each other's intervals and throttling. The fixed .tmp path at lines 74-81 can also collide between writers.
  • Current cache coverage deliberately rejects time-of-day in the system prompt (tests/run_agent/test_run_agent.py:1275-1292), while the system-prompt builder can rebuild after compression (agent/system_prompt.py:507-519). This change needs a specific cache-stability test, not only comments.

Suggested changes

  • Route lifecycle recording through all real session-boundary paths, independently of context_compressor, and add tests for both compressor states.
  • Make timing state session-owned (or explicitly synchronize and document profile-wide semantics), then add temporary-HERMES_HOME concurrency and throttling coverage.

Automated hermes-sweeper review.

Comment thread run_agent.py
# gateway expiry, agent close). Enables Layer 1 dormancy computation.
try:
from agent.time_awareness import on_session_end as _ta_on_end
_ta_on_end()

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This callback is after if not engine: return at line 636, so it never runs for agents without a context compressor. It also does not cover AIAgent.close(), which does not call this helper. Please route lifecycle recording through a compressor-independent session-boundary path.

Comment thread agent/time_awareness.py
now = _now_iso()
now_ts = _now_ts()

last_hb_ts = state.get("last_heartbeat_ts")

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

last_heartbeat_ts is profile-global because _get_time_state_path() always returns one time_state.json. A second live session can suppress this session's heartbeat or make its reported interval describe another session. Please make state ownership session-specific or explicitly synchronize and define profile-wide semantics.

Comment thread agent/system_prompt.py
# byte-stable for the session lifetime.
try:
from agent.time_awareness import on_session_start as _ta_on_start
_wake_ctx = _ta_on_start()

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

build_system_prompt_parts() is also called after context-compression invalidation. Please add a regression test showing that this side-effect and injected value preserve the intended prompt-cache invariant across all rebuild paths.

@k-QRedHacker

Copy link
Copy Markdown
Author

Hi @teknium1, thanks for the thorough review. Your feedback was spot-on and pushed the design from a working prototype to something architecturally sound. I've addressed all three points locally and the subsystem is now running stably:

  1. Compressor-independent hooks: Moved on_session_start to AIAgent.__init__ and on_session_end to AIAgent.close() so they fire regardless of the context engine state.
  2. Session-scoped ownership: Refactored time_state.json to key all data by session_id, eliminating cross-session interference.
  3. Cache invariant: The wake context is computed once at init and stored on self._wake_context. system_prompt.py reads this attribute instead of recomputing, keeping volatile_parts byte-stable across compression rebuilds.

Appreciate the detailed notes.

@teknium1 teknium1 added sweeper:risk-session-state Sweeper risk: may lose/corrupt/mis-associate session or context state sweeper:risk-compatibility Sweeper risk: may break existing users, config, migrations, defaults, or upgrades sweeper:blast-massive Sweeper blast radius: massive — everyone, every turn (invariant surface) labels Jul 11, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

comp/agent Core agent runtime: loop, agent_init, prompt builder, context-compression, responses endpoint duplicate This issue or pull request already exists P3 Low — cosmetic, nice to have sweeper:blast-massive Sweeper blast radius: massive — everyone, every turn (invariant surface) sweeper:risk-caching Sweeper risk: may break/degrade prompt caching or cache-key stability (invariant) sweeper:risk-compatibility Sweeper risk: may break existing users, config, migrations, defaults, or upgrades sweeper:risk-session-state Sweeper risk: may lose/corrupt/mis-associate session or context state type/feature New feature or request

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants