Skip to content

feat: timezone tool support and webhook trigger endpoint#678

Open
zmanian wants to merge 6 commits intonearai:mainfrom
zmanian:tar-sputter
Open

feat: timezone tool support and webhook trigger endpoint#678
zmanian wants to merge 6 commits intonearai:mainfrom
zmanian:tar-sputter

Conversation

@zmanian
Copy link
Collaborator

@zmanian zmanian commented Mar 7, 2026

Summary

Addresses two open issues (rebased from original 6-issue PR; #657/#648/#663/#666 are now in separate PRs):

Test plan

  • cargo clippy --all --all-features passes with zero warnings
  • cargo test --lib passes (2557 tests)
  • 11 new time tool tests covering all timezone operations
  • 2 new webhook handler tests (constant-time comparison, path matching)

Removed from this PR (now in separate PRs)

Closes #662, closes #651

Generated with Claude Code

zmanian and others added 4 commits March 7, 2026 12:01
…earai#657)

On Windows, Instant::now() - Duration panics when system uptime is less
than the subtracted duration. Replace with checked_sub() which returns
None instead of panicking.

Includes regression test that verifies checked_sub doesn't panic even
with an absurdly large duration.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
…earai#666)

The environment variable security option (choice 1) in the setup wizard
didn't initialize secrets_crypto, causing API keys entered afterward to
silently fail to save. Now initializes crypto, auto-saves the master key
to ~/.ironclaw/.env, and shows a masked confirmation of the saved key.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
nearai#662)

- now: accepts optional timezone param, returns utc_iso + local_iso
- convert: new operation for timezone-to-timezone conversion
- format: new operation with strftime formatting + timezone
- parse: accepts naive datetimes with timezone context
- diff: uses flexible timestamp parsing
- Adds chrono-tz dependency for IANA timezone support
- 11 unit tests covering all operations

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
…, and reasoning (nearai#648)

- Gate RuleBasedEvaluator with #[cfg(test)], delete unused LlmEvaluator
- Delete unused chunk_by_paragraphs() and its tests from chunker
- Delete unused install_bundled_channel_from_artifacts() from extensions
- Remove unused safety: Arc<SafetyLayer> field from Reasoning struct
  and propagate removal through all callers (compaction, heartbeat,
  dispatcher, worker, commands, builder, app, registry)

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
@gemini-code-assist
Copy link
Contributor

Summary of Changes

Hello, I'm Gemini Code Assist1! I'm currently reviewing this pull request and will post my feedback shortly. In the meantime, here's a summary to help you and other reviewers quickly get up to speed!

This pull request delivers a set of targeted improvements and essential maintenance, addressing several critical issues and streamlining the codebase. It resolves a Windows-specific panic related to time calculations, enhances the setup wizard's handling of API keys for better security and persistence, and significantly upgrades the time tool with robust timezone capabilities. Furthermore, a substantial dead code cleanup effort has been undertaken, removing obsolete components and simplifying core logic across numerous files.

Highlights

  • Windows Instant Panic Fix: Implemented checked_sub for Instant arithmetic to prevent panics on Windows systems where uptime is less than the subtracted duration.
  • Setup Wizard API Key Handling: Enhanced the setup wizard to automatically initialize secrets_crypto and save the generated master key to ~/.ironclaw/.env when using the environment variable security option.
  • Time Tool Enhancements: Added comprehensive timezone support to the time tool, including convert and format operations, and improved IANA timezone parsing via the chrono-tz crate.
  • Dead Code Cleanup: Removed unused components such as LlmEvaluator, chunk_by_paragraphs, install_bundled_channel_from_artifacts, and the safety field from Reasoning and related structures, significantly reducing code complexity and dependencies.
Changelog
  • Cargo.lock
    • Added chrono-tz and phf 0.12.1 packages and their dependencies.
  • Cargo.toml
    • Added chrono-tz dependency.
  • src/agent/agent_loop.rs
    • Removed the safety parameter from Reasoning::new call.
  • src/agent/commands.rs
    • Removed the safety parameter from HeartbeatRunner::new and Reasoning::new calls.
  • src/agent/compaction.rs
    • Removed SafetyLayer import and the safety field from ContextCompactor.
    • Updated ContextCompactor::new and Reasoning::new to no longer accept a safety parameter.
    • Removed SafetyConfig and SafetyLayer imports from tests.
  • src/agent/cost_guard.rs
    • Modified check_allowed and actions_this_hour to use Instant::checked_sub to prevent panics.
    • Added a regression test for checked_sub to verify panic prevention.
  • src/agent/dispatcher.rs
    • Removed the safety parameter from Reasoning::new calls in both main logic and tests.
    • Removed SafetyConfig and SafetyLayer imports from tests.
  • src/agent/heartbeat.rs
    • Removed SafetyLayer import and the safety field from HeartbeatRunner.
    • Updated HeartbeatRunner::new and spawn_heartbeat to no longer accept a safety parameter.
    • Removed the safety parameter from Reasoning::new calls.
  • src/agent/thread_ops.rs
    • Removed the safety parameter from ContextCompactor::new calls.
  • src/agent/worker.rs
    • Removed the safety parameter from Reasoning::new call.
  • src/app.rs
    • Removed the safety parameter from the tools.register_builder_tool call.
  • src/channels/web/server.rs
    • Modified created_at calculations in tests to use Instant::checked_sub for robustness.
  • src/evaluation/success.rs
    • Removed LlmProvider import.
    • Added #[cfg(test)] attributes to RuleBasedEvaluator struct and its implementations.
    • Removed LlmEvaluator struct and its SuccessEvaluator implementation.
  • src/extensions/manager.rs
    • Removed the install_bundled_channel_from_artifacts function.
  • src/llm/reasoning.rs
    • Removed SafetyLayer import and the safety field from Reasoning.
    • Updated Reasoning::new to no longer accept a safety parameter.
    • Removed SafetyConfig and SafetyLayer imports from tests.
  • src/setup/wizard.rs
    • Modified the environment variable master key setup to initialize secrets_crypto and save the key to ~/.ironclaw/.env.
  • src/tools/builder/core.rs
    • Removed SafetyLayer import and the safety field from LlmSoftwareBuilder.
    • Updated LlmSoftwareBuilder::new and Reasoning::new to no longer accept a safety parameter.
  • src/tools/builtin/time.rs
    • Added chrono-tz import and FixedOffset to chrono imports.
    • Introduced parse_timezone and parse_input_timestamp helper functions.
    • Expanded TimeTool description and parameters_schema to include convert, format, timezone, to_timezone, and format_string options.
    • Implemented convert and format operations, and enhanced now, parse, and diff operations with timezone awareness.
    • Added extensive unit tests for new and updated time tool functionalities, including timezone conversions and formatting.
  • src/tools/mcp/session.rs
    • Modified last_activity calculations in tests to use Instant::checked_sub for robustness.
  • src/tools/registry.rs
    • Removed SafetyLayer import.
    • Removed the safety parameter from register_builder_tool call.
  • src/worker/runtime.rs
    • Removed the safety parameter from Reasoning::new call.
  • src/workspace/chunker.rs
    • Removed the chunk_by_paragraphs function and its associated tests.
  • tests/e2e_routine_heartbeat.rs
    • Removed SafetyConfig and SafetyLayer imports, and updated HeartbeatRunner initialization.
  • tests/heartbeat_integration.rs
    • Removed SafetyLayer import and updated HeartbeatRunner initialization.
Activity
  • No specific activity (comments, reviews, progress updates) was provided for this pull request.
Using Gemini Code Assist

The full guide for Gemini Code Assist can be found on our documentation page, here are some quick tips.

Invoking Gemini

You can request assistance from Gemini at any point by creating a comment using either /gemini <command> or @gemini-code-assist <command>. Below is a summary of the supported commands on the current page.

Feature Command Description
Code Review /gemini review Performs a code review for the current pull request in its current state.
Pull Request Summary /gemini summary Provides a summary of the current pull request in its current state.
Comment @gemini-code-assist Responds in comments when explicitly tagged, both in pull request comments and review comments.
Help /gemini help Displays a list of available commands.

Customization

To customize Gemini Code Assist for GitHub experience, repository maintainers can create a configuration file and/or provide a custom code review style guide (such as PEP-8 for Python) by creating and adding files to a .gemini/ folder in the base of the repository. Detailed instructions can be found here.

Limitations & Feedback

Gemini Code Assist may make mistakes. Please leave feedback on any instances where its feedback is incorrect or counter productive. You can react with 👍 and 👎 on @gemini-code-assist comments. If you're interested in giving your feedback about your experience with Gemini Code Assist for Github and other Google products, sign up here.

You can also get AI-powered code generation, chat, as well as code reviews directly in the IDE at no cost with the Gemini Code Assist IDE Extension.

Footnotes

  1. Review the Privacy Notices, Generative AI Prohibited Use Policy, Terms of Service, and learn how to configure Gemini Code Assist in GitHub here. Gemini can make mistakes, so double check it and use code with caution.

@github-actions github-actions bot added scope: agent Agent core (agent loop, router, scheduler) scope: channel/web Web gateway channel scope: tool Tool infrastructure scope: tool/builtin Built-in tools scope: tool/mcp MCP client scope: tool/builder Dynamic tool builder scope: llm LLM integration scope: workspace Persistent memory / workspace scope: worker Container worker scope: extensions Extension management scope: setup Onboarding / setup scope: evaluation Success evaluation scope: dependencies Dependency updates labels Mar 7, 2026
Copy link
Contributor

@gemini-code-assist gemini-code-assist bot left a comment

Choose a reason for hiding this comment

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

Code Review

This pull request introduces a good set of fixes and improvements. The panic on Windows related to Instant subtraction is correctly addressed using checked_sub, and the new regression test is a great addition. The setup wizard enhancement for API keys and the major improvements to the time tool with timezone support are both excellent. The dead code cleanup also significantly improves maintainability. I've added a few comments regarding the test suite, where using a fallback for time calculations could lead to tests silently passing under incorrect conditions. Explicitly failing the test in these setup scenarios would make them more robust, aligning with the guideline to prefer expect() for test setups dependent on environmental factors.

Note: Security Review did not run due to the size of the PR.

Comment on lines +2623 to +2625
created_at: std::time::Instant::now()
.checked_sub(std::time::Duration::from_secs(600))
.unwrap_or_else(std::time::Instant::now),
Copy link
Contributor

Choose a reason for hiding this comment

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

medium

The use of unwrap_or_else(std::time::Instant::now) could cause this test to silently check the wrong logic. If checked_sub returns None (e.g., on a system with low uptime), created_at will be set to Instant::now(). This would make the session not expired, and the test would proceed to validate the logic for a non-expired session, contrary to the test's intent.

To make the test more robust, it's better to explicitly fail with a clear message if the required test state (a past timestamp) cannot be created.

Suggested change
created_at: std::time::Instant::now()
.checked_sub(std::time::Duration::from_secs(600))
.unwrap_or_else(std::time::Instant::now),
created_at: std::time::Instant::now()
.checked_sub(std::time::Duration::from_secs(600))
.expect("System uptime must be > 10 minutes for this test to be valid"),
References
  1. In tests, when setting up a state that depends on environmental factors (e.g., system uptime for time calculations), prefer expect() to explicitly fail the test with a clear message if the setup is not possible. Avoid fallbacks like unwrap_or() that could cause the test to silently check the wrong logic.

Comment on lines +2732 to +2734
created_at: std::time::Instant::now()
.checked_sub(std::time::Duration::from_secs(600))
.unwrap_or_else(std::time::Instant::now),
Copy link
Contributor

Choose a reason for hiding this comment

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

medium

Similar to the previous comment, using unwrap_or_else here could lead to the test silently passing with the wrong logic if checked_sub underflows. The test is intended to check behavior for an expired session, but the fallback would create a non-expired one.

To ensure the test is reliable, it should panic with a descriptive message if the test setup cannot be achieved.

Suggested change
created_at: std::time::Instant::now()
.checked_sub(std::time::Duration::from_secs(600))
.unwrap_or_else(std::time::Instant::now),
created_at: std::time::Instant::now()
.checked_sub(std::time::Duration::from_secs(600))
.expect("System uptime must be > 10 minutes for this test to be valid"),
References
  1. In tests, when setting up a state that depends on environmental factors (e.g., system uptime for time calculations), prefer expect() to explicitly fail the test with a clear message if the setup is not possible. Avoid fallbacks like unwrap_or() that could cause the test to silently check the wrong logic.

Comment on lines +207 to +209
session.last_activity = std::time::Instant::now()
.checked_sub(std::time::Duration::from_secs(10))
.unwrap_or_else(std::time::Instant::now);
Copy link
Contributor

Choose a reason for hiding this comment

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

medium

This test simulates a stale session by setting last_activity to a time in the past. The unwrap_or_else fallback could cause last_activity to be set to Instant::now() if the subtraction underflows, which would make the session appear fresh. The test would then check the wrong condition and might pass incorrectly.

It's better to make the test fail explicitly if the required past timestamp cannot be created.

Suggested change
session.last_activity = std::time::Instant::now()
.checked_sub(std::time::Duration::from_secs(10))
.unwrap_or_else(std::time::Instant::now);
session.last_activity = std::time::Instant::now()
.checked_sub(std::time::Duration::from_secs(10))
.expect("System uptime must be > 10 seconds for this test to be valid");
References
  1. In tests, when setting up a state that depends on environmental factors (e.g., system uptime for time calculations), prefer expect() to explicitly fail the test with a clear message if the setup is not possible. Avoid fallbacks like unwrap_or() that could cause the test to silently check the wrong logic.

Comment on lines +303 to +305
session.last_activity = std::time::Instant::now()
.checked_sub(std::time::Duration::from_secs(60))
.unwrap_or_else(std::time::Instant::now);
Copy link
Contributor

Choose a reason for hiding this comment

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

medium

The fallback unwrap_or_else(std::time::Instant::now) could cause this test to check the wrong logic. The test intends to push last_activity into the past to observe the change after touch(). If the subtraction underflows, last_activity will be Instant::now(), and the test's assertion might not be testing what's intended.

To make the test more robust, it should fail explicitly if the setup condition cannot be met.

Suggested change
session.last_activity = std::time::Instant::now()
.checked_sub(std::time::Duration::from_secs(60))
.unwrap_or_else(std::time::Instant::now);
session.last_activity = std::time::Instant::now()
.checked_sub(std::time::Duration::from_secs(60))
.expect("System uptime must be > 60 seconds for this test to be valid");
References
  1. In tests, when setting up a state that depends on environmental factors (e.g., system uptime for time calculations), prefer expect() to explicitly fail the test with a clear message if the setup is not possible. Avoid fallbacks like unwrap_or() that could cause the test to silently check the wrong logic.

Comment on lines +367 to +369
let past = std::time::Instant::now()
.checked_sub(std::time::Duration::from_secs(60))
.unwrap_or_else(std::time::Instant::now);
Copy link
Contributor

Choose a reason for hiding this comment

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

medium

This test sets up stale sessions by creating a timestamp in the past. If checked_sub underflows, the fallback to Instant::now() would create non-stale sessions, causing the test to check the wrong logic and potentially pass when it shouldn't.

To ensure the test's correctness, it's better to explicitly fail if the past timestamp cannot be created.

Suggested change
let past = std::time::Instant::now()
.checked_sub(std::time::Duration::from_secs(60))
.unwrap_or_else(std::time::Instant::now);
let past = std::time::Instant::now()
.checked_sub(std::time::Duration::from_secs(60))
.expect("System uptime must be > 60 seconds for this test to be valid");
References
  1. In tests, when setting up a state that depends on environmental factors (e.g., system uptime for time calculations), prefer expect() to explicitly fail the test with a clear message if the setup is not possible. Avoid fallbacks like unwrap_or() that could cause the test to silently check the wrong logic.

@github-actions github-actions bot added size: XL 500+ changed lines risk: high Safety, secrets, auth, or critical infrastructure contributor: core 20+ merged PRs labels Mar 7, 2026
zmanian and others added 2 commits March 7, 2026 12:14
…#663)

Replace all 49 occurrences of datetime('now') with
strftime('%Y-%m-%dT%H:%M:%fZ', 'now') in libSQL migrations so new rows
use the same RFC 3339 format as application code (fmt_ts).

Add tracing::warn to parse_timestamp naive fallback branches so
old-format timestamps are logged when encountered.

5 regression tests covering RFC 3339, naive, and invalid timestamp parsing.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
…gers (nearai#651)

Public endpoint (no auth token) protected by per-routine webhook secrets
with constant-time comparison via subtle::ConstantTimeEq.

Matches path against routines with Trigger::Webhook, validates
X-Webhook-Secret header, and fires the routine through the message
pipeline. Returns 404/401/200 as appropriate.

2 unit tests for secret comparison and path matching.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
@github-actions github-actions bot added the scope: db/libsql libSQL / Turso backend label Mar 7, 2026
@zmanian zmanian changed the title fix: Windows Instant panic, setup wizard API key, timezone tool, dead code cleanup fix: Windows Instant panic, setup wizard, timezone tool, dead code, libSQL timestamps, webhook endpoint Mar 7, 2026
@zmanian zmanian changed the title fix: Windows Instant panic, setup wizard, timezone tool, dead code, libSQL timestamps, webhook endpoint feat: timezone tool support and webhook trigger endpoint Mar 8, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

contributor: core 20+ merged PRs risk: high Safety, secrets, auth, or critical infrastructure scope: agent Agent core (agent loop, router, scheduler) scope: channel/web Web gateway channel scope: db/libsql libSQL / Turso backend scope: dependencies Dependency updates scope: evaluation Success evaluation scope: extensions Extension management scope: llm LLM integration scope: setup Onboarding / setup scope: tool/builder Dynamic tool builder scope: tool/builtin Built-in tools scope: tool/mcp MCP client scope: tool Tool infrastructure scope: worker Container worker scope: workspace Persistent memory / workspace size: XL 500+ changed lines

Projects

None yet

1 participant