Storage: every JS-side datetime('now') write becomes new Date().toISOString()
(messages_out all writers, processing_ack all stamps, delivered, retry
backoff, roles/members/destinations, skill SQL via strftime ISO-Z). Dormant
deliver_after raw string compares wrapped in datetime().
Display: ncl human output renders ISO instants as local "YYYY-MM-DD HH:mm"
(host + container renderers; --json stays ISO; the stamp shape round-trips
through parseZonedToUtc). Task run logs, conversation archive filenames,
clidash, the dashboard pusher, and the uninstall backup stamp all render
local; instruction texts updated in lockstep. CLAUDE.md documents the
convention.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01A2YZQDTw9TQrH3m8NBtVAW
Scheduled tasks move from MCP tools to a first-class ncl resource:
list / get / create / update / cancel / pause / resume / run / append-log,
with args declared on every verb (strict validation, fix-carrying errors)
and deep per-verb help with executable examples.
A series is CronJob-like: the live (pending/paused) row is the next
occurrence, completed rows are its run history. tasks list reads as a
compact run-history table, server-rendered so the container agent prints
the same aligned table. Short named ids (<slug>-<hex>) are
filesystem/thread-safe and copy-pasteable. Agents keep a per-series work
journal: the run log at tasks/<series>.md (append-log + auto-logged
final text).
Each series runs in its own isolated system session
(system:tasks:<series>); the live-task cap is dropped — isolation
replaces throttling. Spent task sessions are GC'd by the sweep.
--script on a task runs a bash gate BEFORE the agent wakes. It prints a
JSON verdict as its last stdout line: {"wakeAgent": bool, "data": {...}}.
wakeAgent:false handles the occurrence without waking the agent; data is
threaded into the fire's prompt, so a gate can fetch/inspect and hand the
agent exactly what changed. Failing scripts back off exponentially
(2·2^(n-1) min, cap 60) and auto-pause the series after 8 consecutive
failures with a host-written run-log note; manual runs are excluded from
the streak.
BREAKING: the schedule_task / list_tasks / update_task / cancel_task /
pause_task / resume_task MCP tools are removed. Agents schedule via
ncl tasks (available under cli_scope=group). Existing task rows keep
firing — storage (messages_in kind='task') is unchanged; only the
management surface moved. Migration: docs/ncl-tasks-migration.md.
Review round (gavrielc):
- Recurrence frequency guard: create/update refuse a recurrence more
frequent than 4 fires/day with a warning steering to gate scripts;
--dangerously-override-recurrence-limit bypasses after explicit user
confirmation. Counted over the next 24h in the instance TZ. Guarded on
update too, so create-slow-then-update cannot sneak past.
- ncl tasks help scripts: resource help topics (new helpTopics seam on
registerResource) carry the full gate-script guide — contract, examples,
state, failure/backoff semantics, testing directions.
- Agent-facing scheduling instructions slimmed: no recurring-frequency
prose in the fragment; one pointer to the help topic.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
POSIX-style TZ strings like IST-2 cause a hard RangeError crash in
formatMessages because Intl.DateTimeFormat only accepts IANA identifiers.
- Add isValidTimezone/resolveTimezone helpers to src/timezone.ts
- Make formatLocalTime fall back to UTC on invalid timezone
- Validate TZ candidates in config.ts before accepting
- Add timezone setup step to detect and prompt when autodetection fails
- Use node:22-slim in Dockerfile (node:24-slim Trixie package renames)
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* feat: per-group timezone architecture with context injection (#483)
Implement a comprehensive timezone consistency layer so the AI agent always
receives timestamps in the user's local timezone. The framework handles all
UTC↔local conversion transparently — the agent never performs manual timezone
math.
Key changes:
- Per-group timezone stored in containerConfig (no DB migration needed)
- Context injection: <context timezone="..." current_time="..." /> header
prepended to every agent prompt with local time and IANA timezone
- Message timestamps converted from UTC to local display in formatMessages()
- schedule_task translation layer: agent writes local times, framework
converts to UTC using per-group timezone for cron, once, and interval types
- Container TZ env var now uses per-group timezone instead of global constant
- New set_timezone MCP tool for users to update their timezone dynamically
- NANOCLAW_TIMEZONE passed to MCP server environment for tool confirmations
Architecture: Store UTC everywhere, convert at boundaries (display to agent,
parse from agent). Groups without timezone configured fall back to the server
TIMEZONE constant for full backward compatibility.
Closes#483Closes#526
Co-authored-by: shawnYJ <shawny011717@users.noreply.github.com>
Co-authored-by: Adrian <Lafunamor@users.noreply.github.com>
Co-authored-by: Claude Opus 4.6 <noreply@anthropic.com>
* style: apply prettier formatting
Co-authored-by: Claude Opus 4.6 <noreply@anthropic.com>
* refactor: strip to minimalist context injection — global TIMEZONE only
Remove per-group timezone support, set_timezone MCP tool, and all
related IPC handlers. The implementation now uses the global system
TIMEZONE for all groups, keeping the diff focused on the message
formatting layer: mandatory timezone param in formatMessages(),
<context> header injection, and formatLocalTime/formatCurrentTime
helpers.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
* refactor: drop formatCurrentTime and simplify context header
Address PR review: remove redundant formatCurrentTime() since message
timestamps already carry localized times. Simplify <context> header to
only include timezone name.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
---------
Co-authored-by: shawnYJ <shawny011717@users.noreply.github.com>
Co-authored-by: Adrian <Lafunamor@users.noreply.github.com>
Co-authored-by: Claude Opus 4.6 <noreply@anthropic.com>