Repository navigation
Upgrade notes
- Schema migration 0012 runs at startup on SQLite and Postgres. It adds two
watcher columns, a report table and an append-only dispatch audit table.
Take a backup first: on the first start the watcher's old no-op run rows
(trigger-lessmanualruns with no proposal) are deleted, and that can't be
undone. The startup log linewatcher_maintenancereports what was removed. - Existing watchers keep Continue on and Notify off. Existing
autowatchers
becomeask_on_risk, and requests that were waiting for an answer are
superseded. To useautoagain, confirm it from the dashboard and turn off
"Pause automatic prompt sending". - Approving a watcher proposal now sends the prompt to sessions AgentPulse
launched (see Changed). - Telegram channels enrolled in a group chat must be re-linked before they can
approve or ask; private-chat channels keep working. In team mode, channels
created before this release refuse approvals until an admin re-links them. - With authentication disabled, the same-origin check for state-changing
routes accepts a request only on a loopback or allowlisted host. - The session-summary call limit rises from 120 to 360 seconds
(AGENTPULSE_SESSION_SUMMARY_TIMEOUT_SECONDS); set the same value on every
replica.
Added
-
Notify mode. A watcher can now write a two-line status note for you when a turn ends
(finished, looks stuck, or waiting for you), from what happened since the last
note. It appears in the AI tab, as a new inbox item that clears when you open the
session, and, if you assign a channel, in Telegram. It never sends anything to the agent. New
watchers start with Notify on and Continue off; change that under Settings, AI
watcher ("New watchers start with"), and per session in the AI tab. -
Pause automatic prompt sending (Settings, AI watcher), on by default. While it is on, a
session set toautoopens a request for you instead of sending. -
A dispatch audit trail. Every prompt the watcher sent, or refused to send, is recorded in
an append-only table that is not deleted with the session. -
Telegram message detail per channel: Minimal (the default: session name, verdict and a
generic reason) or Full (adds the note and the suggested prompt, redacted). Full is never
available for a group chat. Telegram is a third-party service. -
Telegram channels can be re-linked from Settings without losing the sessions that point at
them. -
AGENTPULSE_WATCHER_TIMEOUT_SECONDS(default90, range 10 to 1800): the time limit of each
watcher model call. -
A provider for quick calls. Settings, AI: "Use for quick calls" on a
provider sends the short helper calls (Ask intent checks, query expansion,
alert-rule checks) to it first, so they no longer queue behind a long call
such as a session summary on a single model server. Answers and summaries stay
on the default provider. If the quick provider fails or times out, the call is
repeated once on the default provider and the quick provider is skipped for a
minute. The quick provider gets its own short limit. Unset keeps today's behaviour. -
AGENTPULSE_ASK_ANSWER_TIMEOUT_SECONDS(default180, range 30 to 1800): the
time limit of the model call that writes an Ask answer and of a per-session
Q&A answer. The Q&A limit was 60 seconds. -
AGENTPULSE_AI_FAST_PROVIDER_TIMEOUT_SECONDS(default5, range 1 to 60): the
time limit of the attempt on the quick-calls provider, before the call is
repeated on the default provider. -
AGENTPULSE_AI_HELPER_TIMEOUT_SECONDS(default20, range 5 to 300): the
time limit of the short helper calls (Ask intent classifiers, query expansion,
alert-rule yes/no check). They were 8 and 15 seconds. -
AGENTPULSE_SESSION_SUMMARY_TIMEOUT_SECONDS(default360, range 30 to 1800):
the time limit of one session-summary model call, per call (the repair call
gets it in full). Use the same value on every replica. See the README's
"Session summary" section.
Changed
- ⚠ breaking: approving a watcher proposal now sends the prompt. For a session AgentPulse
launched, Approve (in the app or in Telegram) types the prompt into the agent. For any other
session, Approve is replaced by Copy prompt and Dismiss, and the Telegram message has no
Approve button. Approval is refused if the session has moved on, the request expired (20
minutes), or the text changed after you opened it. An approval is queued for the host
even if it is offline at that moment; onlyautorequires a live host. - ⚠ breaking: the
autopolicy sends only fixed nudges. It can send "retry the failed
step", "run the tests" or "summarize status" and nothing the model wrote. It must be confirmed
from the dashboard (not with an API key), is paused until "Pause automatic prompt sending"
is turned off, and is limited to 6 sends an hour per session and 20 overall. Editing the
system prompt, provider or limit asks for a new confirmation. Existingautowatchers were
switched toask_on_risk. - ⚠ breaking:
autonever sends while the agent is asking a question or a permission prompt
is open, and a "keep going" nudge (proceed) is no longer sent automatically. - ⚠ breaking: Telegram taps and messages are accepted only from the enrolled account in a
private chat. Channels enrolled in a group must be re-linked to approve or ask from
Telegram; channels enrolled in a private chat keep working. In team mode a channel belongs to
the person who created it, and only that person can re-link it; channels created before this
release belong to nobody, refuse approvals until an admin re-links them, and the admin who
does becomes the owner. Enrollment codes are longer and expire after 15 minutes. - The watcher now wakes when a turn ends (after a 10 second wait for a follow-up) instead of on
every event, for sessions that report through hooks as well as managed ones. - "Max continuations" now counts automatic sends only; approving a suggestion yourself never
uses it up, and re-enabling a watcher no longer resets it (the AI tab has a Reset button). - Changing a session's watcher and deciding a proposal are checked against the session's owner
(or an admin) in team mode, validate their input strictly, and are written to the audit log.
The AI pause and kill switches stay global settings that only a team admin can change.
Turningautoon, resetting the counter and switching the pause or kill off need a signed-in
person, not an API key. - Every Telegram message the watcher sends is redacted of secrets and has home paths shortened.
- Session summary time bounds follow the new call limit, for every install.
The per-call limit rises from 120 to 360 seconds, the lease on a running
summary from 300 to 780 seconds (2 x the limit + 60), and the watchdog from
5 to 13 minutes. A hung call now holds a generation slot for 13 minutes, and on
Postgres a stuckgeneratingrow is recovered only at the lease. The failure
breaker window is the longer of 10 minutes and two leases and a minute (27
minutes at the default), and a forgotten subject is dropped after the longer
of 60 minutes and that window. A running generation is aborted when it is
released (watchdog, lease or shutdown), so its connection closes. - LLM responses are capped: a success body or stream at 8 MiB, an error body
at 4 KiB. Over the cap is a provider error.
Fixed
- The watcher never woke for sessions that report through hooks. It listened for events
only managed sessions emit; a hook-observed session was never watched. - The max-continuations cap never applied, and reset when a watcher was re-enabled.
- Inbox approvals skipped the safety filter that the other approval routes applied.
- Telegram approvals, action buttons and Ask messages were not tied to their chat or user,
and the enrollment code was short, never expired and had no failure limit. Any operator could
list every Telegram channel and see pending enrollment codes. - Proposals never expired, a stuck pending proposal stayed pending forever, and a corrupted
kill-switch value did not stop AI. - The watcher no longer leaves one no-op run row per event: existing rows are removed once at
startup (see the upgrade notes). - Ask no longer shows a model's truncated reasoning as its answer. A model
that thinks before it answers could spend its whole output budget on
reasoning and return an empty reply; the OpenAI-compatible adapter then
passed the reasoning text on as the answer. An empty reply is now an error
(with the finish reason in the message) for every caller, streamed or not.
Every call that produces an answer or a decision (the Ask answer, per-session
Q&A, query expansion, the alert-rule yes/no check, the watcher) now asks the
provider to skip its reasoning phase, as the session summary and the intent
classifiers already did. - A failed query expansion no longer costs every Ask question its timeout.
The optional expansion step now asks with reasoning off and a small token
budget, and after a failure it is skipped for two minutes. - "What sessions are going?" answers from the Working state. It was
searched as the text "are going" and listed any session whose events
contained the word. Phrasings that mean the state (going, running, working,
in progress, right now, "what's running") now list the sessions the
dashboard shows as Working, filler words no longer become a search, and a
classifier failure note no longer repeats the classifier's internal error. - Ask follow-ups keep their subject. A message that points
back ("what did it change?", "and the other one?") starts from the sessions
the previous answer was built from instead of resolving on its own. A new
subject the message names outranks them. - The "stuck" badge no longer fires on sessions that merely mention errors.
The repeated-failure rule counted any tool output containing "error",
"failed" or "exception". It now counts failed tool calls (a failure hook the
user did not interrupt, or a non-zero exit code), which also catches real
failures the old rule missed. Inbox and digest entries that came from the old
rule clear on their own as sessions are re-evaluated. - Vector search no longer stops indexing new events while the server runs.
Events delivered by hooks were only embedded by a backfill at startup, so the
index fell behind until the next restart, and a provider outage during that
backfill left a backlog. A background pass now runs every minute in small
batches (100 batches of up to 32 events at most, a short pause after each),
and works off any backlog on its own. While the embedding provider is down
it retries after a growing wait (up to 5 minutes), each time with a single
probe request first, and resumes by itself when the provider returns; no
restart or rebuild is needed. - Session summaries on slow local models no longer always time out.
A summary call longer than 300 seconds was cut by Bun's own fetch limit
whatever the configured timeout; that limit is now lifted, so the configured
limit is the real one. A provider that stops sending in the middle of an
answer is now a timeout (the Summary tab says so) rather than an internal
error, and the same applies to the AI watcher, Ask and the classifier. An
immediate provider 5xx (including 502, 504 and 524) is reported as a provider
error, no longer as a timeout. The failed-summary message now names the
current model and limit and the setting that raises it. The unknown-outcome
daily ceiling is charged to the day the call started, not the day it ended.