Skip to content

v0.9.0 — Watcher Notify and Continue, AI reliability

Latest

Choose a tag to compare

@jstuart0 jstuart0 released this 25 Oct 02:02
e63a1c2

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-less manual runs with no proposal) are deleted, and that can't be
    undone. The startup log line watcher_maintenance reports what was removed.
  • Existing watchers keep Continue on and Notify off. Existing auto watchers
    become ask_on_risk, and requests that were waiting for an answer are
    superseded. To use auto again, 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 to auto opens 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 (default 90, 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 (default 180, 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 (default 5, 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 (default 20, 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 (default 360, 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; only auto requires a live host.
  • ⚠ breaking: the auto policy 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. Existing auto watchers were
    switched to ask_on_risk.
  • ⚠ breaking: auto never 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.
    Turning auto on, 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 stuck generating row 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.