Skip to content

feat(agents): long-term agents (rail, trusted thread, tasks, triggers, webhooks) - #1087

Open
SoulKyu wants to merge 93 commits into
agegr:mainfrom
SoulKyu:feat/long-term-agents
Open

SoulKyu wants to merge 93 commits into
agegr:mainfrom
SoulKyu:feat/long-term-agents

Conversation

@SoulKyu

@SoulKyu SoulKyu commented Oct 7, 2026

Copy link
Copy Markdown

Summary

Long-term agents: persistent agents with their own rail, one pinned continuous thread each, an automatic home directory, scheduled and queued tasks, webhook alerts handled in isolated read-only runs, and a memory section backed by pi-mem0 (optional).

The PR also contains the Agent Ops foundation the feature is built on (task store, runner, scheduler, triggers, webhook ingestion, memory approval queue). Agent Ops never shipped on its own: its overlay is removed by the last commits and its routes now serve the agents view.

Stacked on #1069 (start a session as an agent profile) and #1071 (providers registered at session_start); their commits appear here until they merge.

What the user sees

  • Rail (left, horizontal strip on mobile): one avatar per agent, unread badge, running dot, + to create, ☰ back to sessions. ?agent=<name> deep-links to an agent.
  • Creation form: name, avatar, role, model, reasoning level, tools preset. The home ~/.pi/agent/agents-home/<name> is created (mode 700) and listed in the agent's file tree.
  • Thread: one session per agent, created trusted in its home, reopened forever. Profile edits apply live (model, reasoning) or at the next idle (role, tools). Delete moves home and thread to .trash.
  • Right panel: status and context usage, memory to approve, tasks (+ queue a task), recent memories with forget.
  • Events: scheduled runs and queued tasks post a card in the thread followed by the prompt; webhook alerts run isolated (read-only tool allowlist, fenced payload) and post a display-only summary card with see the run, which opens the run read-only. The agent learns about an alert only when the user replies to its card.
  • Notifications: unread badge always; web push only for agent_notify (a tool the agent gets in its trusted thread) and failed runs. The normal completion push is suppressed for agent sessions.

Trust model

  • A session's trust is written once, by startRpcSession, into a pi-web:agent-profile entry; absent or malformed means untrusted. Only the thread opener passes trusted.
  • Trusted threads and isolated runs resolve the global long-term profile only, in the agent home: an agent can write its home, so a project profile there can never shadow it.
  • Isolated runs are narrowed to read grep find ls memory_search memory_save (agentProfileTools), re-checked on the live get_tools, and refuse every non-get_* command over the API (403). Their sessions open read-only in the UI.
  • Event and summary cards are type: "custom" entries: displayed, never part of the model context. Summaries are redacted before they are written.
  • Long-term profiles are hidden from Settings › Sub-agents and from the Agent tool, and refuse delegation.
  • Triggers belong to long-term agents (no cwd); schedules run in the thread, webhooks run isolated; PATCH /api/agents/[name] re-pins the agent's triggers, delete removes them and cancels queued tasks.

Memory (optional)

The memory sections rely on a memory extension that implements the small file contract documented in docs/agents/long-term-agents.md (a read-only snapshot per agent, forget requests, a staging directory for facts to approve); my pi-mem0 extension does. A trusted thread saves directly into the agent's scope; untrusted runs stage facts for approval. Pi Web only reads the snapshot and writes forget requests; it never touches the store. Without such an extension the memory sections stay empty and nothing else changes.

Docs

docs/agents/long-term-agents.md (data model, thread, security rules, events, webhooks, known gaps) and the updated docs/agents/agent-ops.md; AGENTS.md file map.

Testing

  • npm run lint, npx tsc --noEmit, full test suite (node:test) green on the branch.
  • Manual smoke on a dev server: create an agent, thread reply, profile edit applied, queue a task while idle and while replying, 1-minute schedule, agent_notify, cancel a running task, pin drift refused then re-pinned, webhook → isolated run with the allowlist only → summary card → read-only view (403 on prompt, 200 on get_state) → approval queue → recent memories.
  • Deferred providers (pi-claude-bridge): thread starts on the default model and switches after session_start, keeping the profile's reasoning level.

Known gaps

Listed in docs/agents/long-term-agents.md › Known gaps (pid reuse after a restart can keep a task lock, duplicated tasks poll between the two panels, terminal tasks of a deleted agent still list under a recreated agent of the same name).

SoulKyu added 30 commits October 5, 2026 07:37
Add an agent selector to the new-session composer. Picking a profile starts
a top-level session with the profile's prompt, tools, model and thinking,
reusing the isolated resources subagents already run with.

- persist the profile in a pi-web:agent-profile custom entry so reopening
  the session restores it, and expose it on GET /api/sessions/[id]
- POST /api/agent/new accepts agentProfile; a profile fixes the tools, so
  set_tools is refused and the tool preset control is hidden
- extract resolveProfileActiveTools() shared with the subagent runtime
- completion notifications stay on for these sessions (they have no parent)
pi-web builds a fresh ModelRuntime for every model listing and never starts
a session in it. Extensions that register their provider only in the first
runtime of a process and defer later ones to session_start (pi-claude-bridge)
disappeared from the picker after the first listing, and a session could not
start on their models.

Remember the available models of every runtime pi-web builds and add back
providers a runtime does not register at all. A session whose requested or
restored model is deferred starts on the default and switches once its
extensions are bound.
SoulKyu added 30 commits October 6, 2026 16:13
…the thread; webhook runs narrowed to the allowlist
…ummaries; cancel queued tasks on delete; fail closed on a missing profile
This reverts commit 19244cd: the loopback default stays upstream.

This branch has not been deployed

No deployments
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant