diff --git a/CHANGELOG.md b/CHANGELOG.md index 963c58f..e8e2c2f 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,11 @@ # Changelog +## 0.9.0 - 2026-07-11 + +- Added safe, bounded tool-call progress cards with active, completed, and failed lifecycle aggregation, approval-aware publication, allowlisted intent summaries, redaction, and robust missing-ID correlation. #80 +- Stabilized scoped broker startup, route ownership, and reconnect registration, including migration of legacy lock files that could stall startup with `ENOTDIR`. #81 #83 +- Improved session cleanup and selection across messengers by hiding superseded same-workspace sessions by default, adding `/sessions all`, and providing Telegram `Use` and `Forget` actions. #81 + ## 0.8.0 - 2026-06-14 - Added cross-messenger live progress updates so supported Slack, Discord, and Telegram destinations update an existing progress message where possible instead of posting repeated snapshots. #78 diff --git a/openspec/changes/improve-tool-call-progress-reporting/.openspec.yaml b/openspec/changes/archive/2026-07-11-improve-tool-call-progress-reporting/.openspec.yaml similarity index 100% rename from openspec/changes/improve-tool-call-progress-reporting/.openspec.yaml rename to openspec/changes/archive/2026-07-11-improve-tool-call-progress-reporting/.openspec.yaml diff --git a/openspec/changes/improve-tool-call-progress-reporting/design.md b/openspec/changes/archive/2026-07-11-improve-tool-call-progress-reporting/design.md similarity index 100% rename from openspec/changes/improve-tool-call-progress-reporting/design.md rename to openspec/changes/archive/2026-07-11-improve-tool-call-progress-reporting/design.md diff --git a/openspec/changes/improve-tool-call-progress-reporting/proposal.md b/openspec/changes/archive/2026-07-11-improve-tool-call-progress-reporting/proposal.md similarity index 100% rename from openspec/changes/improve-tool-call-progress-reporting/proposal.md rename to openspec/changes/archive/2026-07-11-improve-tool-call-progress-reporting/proposal.md diff --git a/openspec/changes/improve-tool-call-progress-reporting/specs/messenger-relay-sessions/spec.md b/openspec/changes/archive/2026-07-11-improve-tool-call-progress-reporting/specs/messenger-relay-sessions/spec.md similarity index 100% rename from openspec/changes/improve-tool-call-progress-reporting/specs/messenger-relay-sessions/spec.md rename to openspec/changes/archive/2026-07-11-improve-tool-call-progress-reporting/specs/messenger-relay-sessions/spec.md diff --git a/openspec/changes/improve-tool-call-progress-reporting/tasks.md b/openspec/changes/archive/2026-07-11-improve-tool-call-progress-reporting/tasks.md similarity index 100% rename from openspec/changes/improve-tool-call-progress-reporting/tasks.md rename to openspec/changes/archive/2026-07-11-improve-tool-call-progress-reporting/tasks.md diff --git a/openspec/changes/stabilize-broker-and-session-lifecycle/.openspec.yaml b/openspec/changes/archive/2026-07-11-stabilize-broker-and-session-lifecycle/.openspec.yaml similarity index 100% rename from openspec/changes/stabilize-broker-and-session-lifecycle/.openspec.yaml rename to openspec/changes/archive/2026-07-11-stabilize-broker-and-session-lifecycle/.openspec.yaml diff --git a/openspec/changes/stabilize-broker-and-session-lifecycle/design.md b/openspec/changes/archive/2026-07-11-stabilize-broker-and-session-lifecycle/design.md similarity index 100% rename from openspec/changes/stabilize-broker-and-session-lifecycle/design.md rename to openspec/changes/archive/2026-07-11-stabilize-broker-and-session-lifecycle/design.md diff --git a/openspec/changes/stabilize-broker-and-session-lifecycle/proposal.md b/openspec/changes/archive/2026-07-11-stabilize-broker-and-session-lifecycle/proposal.md similarity index 100% rename from openspec/changes/stabilize-broker-and-session-lifecycle/proposal.md rename to openspec/changes/archive/2026-07-11-stabilize-broker-and-session-lifecycle/proposal.md diff --git a/openspec/changes/stabilize-broker-and-session-lifecycle/specs/messenger-command-surfaces/spec.md b/openspec/changes/archive/2026-07-11-stabilize-broker-and-session-lifecycle/specs/messenger-command-surfaces/spec.md similarity index 100% rename from openspec/changes/stabilize-broker-and-session-lifecycle/specs/messenger-command-surfaces/spec.md rename to openspec/changes/archive/2026-07-11-stabilize-broker-and-session-lifecycle/specs/messenger-command-surfaces/spec.md diff --git a/openspec/changes/stabilize-broker-and-session-lifecycle/specs/messenger-relay-sessions/spec.md b/openspec/changes/archive/2026-07-11-stabilize-broker-and-session-lifecycle/specs/messenger-relay-sessions/spec.md similarity index 100% rename from openspec/changes/stabilize-broker-and-session-lifecycle/specs/messenger-relay-sessions/spec.md rename to openspec/changes/archive/2026-07-11-stabilize-broker-and-session-lifecycle/specs/messenger-relay-sessions/spec.md diff --git a/openspec/changes/stabilize-broker-and-session-lifecycle/specs/relay-broker-topology/spec.md b/openspec/changes/archive/2026-07-11-stabilize-broker-and-session-lifecycle/specs/relay-broker-topology/spec.md similarity index 100% rename from openspec/changes/stabilize-broker-and-session-lifecycle/specs/relay-broker-topology/spec.md rename to openspec/changes/archive/2026-07-11-stabilize-broker-and-session-lifecycle/specs/relay-broker-topology/spec.md diff --git a/openspec/changes/stabilize-broker-and-session-lifecycle/tasks.md b/openspec/changes/archive/2026-07-11-stabilize-broker-and-session-lifecycle/tasks.md similarity index 100% rename from openspec/changes/stabilize-broker-and-session-lifecycle/tasks.md rename to openspec/changes/archive/2026-07-11-stabilize-broker-and-session-lifecycle/tasks.md diff --git a/openspec/specs/messenger-command-surfaces/spec.md b/openspec/specs/messenger-command-surfaces/spec.md index f68183b..bf4ef38 100644 --- a/openspec/specs/messenger-command-surfaces/spec.md +++ b/openspec/specs/messenger-command-surfaces/spec.md @@ -141,3 +141,21 @@ The system SHALL expose remote skill discovery and invocation through stable PiR - **THEN** labels and descriptions are truncated or paginated according to platform limits - **AND** they include only safe skill name, description, and source category metadata permitted by skill exposure policy +### Requirement: Session-list action surfaces prioritize actionable controls +Messenger command surfaces SHALL keep session-list buttons, menus, and equivalent action affordances aligned with the session state and the canonical command behavior they invoke. + +#### Scenario: Offline session action is rendered +- **WHEN** a messenger renders buttons or menu actions for an offline session row +- **THEN** the primary action is a safe cleanup or inspection action such as `/forget` or `/status` +- **AND** the action does not advertise prompt delivery, recent live progress, or switching as if the offline session were reachable + +#### Scenario: Recent command remains available outside default row buttons +- **WHEN** the default session-list action surface omits per-row `Recent` actions +- **THEN** the messenger still supports `/recent` or `/activity` through the canonical command parser when that command is available for the platform +- **AND** command-surface parity tests treat the command as supported even if it is not present as a default row button + +#### Scenario: Platform cannot fit all actions +- **WHEN** a messenger platform has limited button or menu space for session-list actions +- **THEN** PiRelay prioritizes session selection for online non-current sessions and cleanup for offline/superseded sessions before optional recent-activity shortcuts +- **AND** omitted optional shortcuts remain documented through text commands or detailed status views + diff --git a/openspec/specs/messenger-relay-sessions/spec.md b/openspec/specs/messenger-relay-sessions/spec.md index bc558f3..1eaeef4 100644 --- a/openspec/specs/messenger-relay-sessions/spec.md +++ b/openspec/specs/messenger-relay-sessions/spec.md @@ -955,3 +955,157 @@ The system SHALL deliver non-terminal live progress as an updated per-destinatio - **THEN** live progress update/edit behavior respects each binding's existing progress-mode eligibility independently - **AND** quiet receives no live progress while completion-only receives no ordinary live progress +### Requirement: Workspace-aware stale session presentation +The system SHALL reduce stale session-list clutter by identifying older offline bindings from the same machine and workspace as superseded when a newer online session for that workspace exists. + +#### Scenario: Newer online session supersedes older offline same-workspace bindings +- **WHEN** an authorized user requests `/sessions` and there is a newer online session for the same machine and workspace as one or more older offline bindings +- **THEN** the default session list shows the newer online session +- **AND** the older offline same-workspace bindings are hidden or marked superseded by default +- **AND** ordinary prompt routing does not target the superseded offline bindings unless the user explicitly selects them in an all-sessions or cleanup view + +#### Scenario: Offline session has no online same-workspace replacement +- **WHEN** an authorized user requests `/sessions` and an offline binding has no newer online sibling for the same machine and workspace +- **THEN** the system may show that offline binding as an offline session so the user can understand and clean up the pairing + +#### Scenario: Superseded sessions remain inspectable +- **WHEN** an authorized user requests `/sessions all`, `relay sessions all`, or an equivalent explicit all-sessions or diagnostic session list +- **THEN** the system includes superseded offline bindings with clear stale/superseded wording +- **AND** it does not expose raw session file paths, hidden prompts, tool internals, bot tokens, or transcripts + +#### Scenario: Superseded session can be forgotten +- **WHEN** an authorized user invokes `/forget ` for a superseded offline binding +- **THEN** the system revokes or removes that binding according to existing forget semantics +- **AND** the binding no longer appears in default or all-sessions lists for that messenger identity + +#### Scenario: Workspace grouping is unavailable +- **WHEN** the system cannot safely determine a workspace identity for a binding or route +- **THEN** it falls back to existing session selection/listing behavior for that entry +- **AND** it does not guess supersession solely from an ambiguous display label + +### Requirement: Session list actions are primary-task oriented +The system SHALL render session-list buttons and equivalent platform actions according to the useful next action for each session state. + +#### Scenario: Current online session is listed +- **WHEN** the session list includes the currently selected online session +- **THEN** the default actions identify it as current or active +- **AND** they prioritize status or relevant live controls over switching to itself + +#### Scenario: Non-current online session is listed +- **WHEN** the session list includes an online session that is not the current active selection +- **THEN** the default actions include a way to select that session +- **AND** they MAY include a platform-appropriate one-shot prompt affordance when safe and supported + +#### Scenario: Offline session is listed +- **WHEN** the session list includes an offline or superseded session +- **THEN** the default action SHOULD be cleanup-oriented, such as forgetting that offline binding +- **AND** tapping the row action MUST NOT imply that an offline prompt or control succeeded + +#### Scenario: Busy current session is listed +- **WHEN** the currently selected session is online and busy +- **THEN** detailed session controls MAY prioritize steer, follow-up, abort, or compact actions according to existing authorization and delivery rules +- **AND** they do not bypass pause, revocation, or route availability checks + +### Requirement: Recent activity remains explicit command-level inspection +The system SHALL keep `/recent` and `/activity` as explicit commands for safe recent relay activity inspection while not requiring them as default per-session list buttons. + +#### Scenario: User requests recent activity explicitly +- **WHEN** an authorized user invokes `/recent`, `/activity`, or an equivalent explicit recent-activity command for the current or resolved session +- **THEN** the system returns recent safe relay activity according to existing progress/activity redaction and bounded retention rules + +#### Scenario: Session list is rendered +- **WHEN** the system renders a default session-list button grid or equivalent action list +- **THEN** it is not required to include a `Recent` action for every session row +- **AND** removing those buttons MUST NOT remove the `/recent` or `/activity` text command behavior + +### Requirement: Tool progress is summarized safely +The system SHALL summarize tool-call progress using safe, bounded, human-readable operation labels instead of exposing raw tool arguments, tool output, transcripts, hidden prompts, pairing codes, raw messenger destination identifiers, or secrets. + +#### Scenario: Bash progress shows command intent safely +- **WHEN** a running paired session emits a bash tool call and the receiving binding is eligible for normal progress +- **THEN** the progress update includes a bounded redacted summary of the command intent, such as the first command line +- **AND** the update does not include command output, hidden prompts, full transcripts, bot tokens, or unredacted secret-pattern matches + +#### Scenario: File tools show target paths without content +- **WHEN** a running paired session emits read, edit, or write tool calls and the receiving binding is eligible for progress +- **THEN** the progress update identifies the relevant target file path or safe basename when available +- **AND** the update does not include file contents, replacement text, patches, or unbounded arguments + +#### Scenario: Search/list tools show query or path intent safely +- **WHEN** a running paired session emits grep, rg, find, or ls style tool calls and the receiving binding is eligible for progress +- **THEN** the progress update includes a bounded redacted search pattern, query, or target path when available +- **AND** unknown or unsafe fields are omitted rather than serialized generically + +#### Scenario: Unknown tools remain conservative +- **WHEN** a running paired session emits an unknown or custom tool call +- **THEN** normal-mode progress identifies only the sanitized tool name or a conservative generic label +- **AND** it does not serialize arbitrary tool arguments + +### Requirement: Tool progress is aggregated by turn +The system SHALL aggregate current-turn tool progress by stable tool-call identity and tool kind so repeated tool events produce a compact live status rather than many generic completion messages. + +#### Scenario: Repeated tool calls collapse into counts +- **WHEN** a running paired session completes multiple tool calls of the same kind within the progress window +- **THEN** the progress update includes a bounded aggregate count such as `bash×2` or `read×4` +- **AND** it does not send a separate messenger message for every individual completion when the adapter can coalesce or edit live progress + +#### Scenario: Active and recent tools are shown compactly +- **WHEN** one or more tool calls are active or recently completed during a running paired turn +- **THEN** the progress update prioritizes the current active tool summaries and the most recent completed or failed summaries within the configured progress length limit +- **AND** older repeated activity is represented by aggregate counts rather than unbounded rows + +#### Scenario: Failed tools are visible without leaking output +- **WHEN** a tool call fails during a running paired turn and the receiving binding is eligible for progress +- **THEN** the progress update marks that tool as failed using the safe tool label +- **AND** it does not include raw stack traces, command output, or unbounded error payloads in normal mode + +#### Scenario: Approval-gated tools are published only after authorization +- **WHEN** a tool execution lifecycle starts before its blockable approval-gate check completes +- **THEN** PiRelay may stage safe local progress state but does not publish the tool as active before the gate allows execution +- **AND** denied, expired, undeliverable, or unauthorized approval requests do not leave active or failed tool-progress rows + +#### Scenario: Tool progress resets on turn boundaries +- **WHEN** a Pi turn starts, completes, fails, aborts, unregisters, or the runtime restarts +- **THEN** current-turn tool progress state is reset or discarded +- **AND** later turns do not display stale tool rows or counts from previous turns + +### Requirement: Progress modes govern tool reporting +The system SHALL apply existing progress-mode semantics to improved tool reporting for every messenger binding independently. + +#### Scenario: Normal mode receives low-noise tool summaries +- **WHEN** a binding is configured for normal progress and a running paired session emits tool activity +- **THEN** the binding receives coalesced safe tool summaries and aggregate counts +- **AND** it does not receive generic duplicate `Processed tool result` or repeated `Tool completed — ` messages for the same activity + +#### Scenario: Verbose mode remains bounded and safe +- **WHEN** a binding is configured for verbose progress and a running paired session emits tool activity +- **THEN** the binding may receive additional safe technical tool lifecycle detail +- **AND** the output remains redacted, bounded, coalesced, and free of raw tool output or arbitrary argument serialization + +#### Scenario: Completion-only excludes ordinary tool progress +- **WHEN** a binding is configured for completion-only progress and a running paired session emits ordinary tool activity +- **THEN** the binding does not receive the ordinary tool progress update +- **AND** terminal completion output and allowed compaction notifications continue to follow their existing policies + +#### Scenario: Quiet suppresses all tool progress +- **WHEN** a binding is configured for quiet progress and a running paired session emits tool activity +- **THEN** the binding receives no tool progress update + +### Requirement: Tool progress delivery preserves adapter parity +The system SHALL deliver improved tool progress through the same shared progress pipeline for Telegram direct runtime, Telegram broker runtime, Slack runtime, Discord runtime, and future messenger adapters. + +#### Scenario: Telegram updates live tool progress in place when possible +- **WHEN** Telegram can edit the live progress message and the tool-progress card changes +- **THEN** PiRelay updates the existing live progress message instead of posting a new message for every tool call +- **AND** if editing fails, PiRelay falls back to bounded coalesced snapshots without exposing unsafe data + +#### Scenario: Slack and Discord use coalesced snapshots +- **WHEN** Slack or Discord receives improved tool progress +- **THEN** PiRelay sends bounded coalesced snapshots using the same safe summaries and progress-mode filtering as Telegram +- **AND** authorization, paused/revoked binding checks, and destination scoping remain enforced before delivery + +#### Scenario: Broker and in-process runtimes match +- **WHEN** equivalent tool progress is emitted through the in-process runtime and broker-owned Telegram runtime +- **THEN** both paths produce equivalent safe tool summary content and progress-mode behavior +- **AND** neither path persists raw tool args, tool outputs, or secret-bearing semantic keys + diff --git a/openspec/specs/relay-broker-topology/spec.md b/openspec/specs/relay-broker-topology/spec.md index cf005e2..5005259 100644 --- a/openspec/specs/relay-broker-topology/spec.md +++ b/openspec/specs/relay-broker-topology/spec.md @@ -4,20 +4,31 @@ Defines the local and federated PiRelay broker topology for route registration, ingress ownership, peer communication, failover, and duplicate-bot safety across machines and sessions. ## Requirements ### Requirement: Machine-local broker supervisor -The system SHALL run at most one authoritative PiRelay broker per machine for a configured PiRelay state directory. +The system SHALL run at most one authoritative PiRelay broker per machine for a configured broker scope consisting of the PiRelay state directory, bot/account token hash, and optional broker namespace. #### Scenario: First Pi session starts on a machine -- **WHEN** a Pi session starts with PiRelay enabled and no local broker is running for the configured state directory +- **WHEN** a Pi session starts with PiRelay enabled and no local broker is running for the configured broker scope - **THEN** the system starts one local broker and registers the session route with that broker #### Scenario: Additional Pi sessions start on the same machine -- **WHEN** another Pi session starts on the same machine with the same PiRelay state directory +- **WHEN** another Pi session starts on the same machine with the same broker scope - **THEN** it connects to the existing local broker instead of starting another broker +#### Scenario: Concurrent Pi sessions start on the same machine +- **WHEN** two or more Pi sessions with the same broker scope attempt to start PiRelay while the local broker socket is not ready +- **THEN** broker startup is serialized with an inter-process supervisor lock +- **AND** only one broker process is spawned for that broker scope +- **AND** all sessions connect to that broker and register their routes there + #### Scenario: Stale broker socket exists - **WHEN** the local broker socket or pid file exists but the broker is not alive - **THEN** the system safely removes the stale local coordination file and starts a new broker without losing persisted bindings +#### Scenario: Live pid exists while socket is not ready +- **WHEN** a broker pid for the configured broker scope is live but the socket is not yet accepting connections +- **THEN** later Pi sessions wait for the socket to become ready instead of spawning a competing broker +- **AND** startup failure is reported as a secret-safe diagnostic if readiness times out + ### Requirement: Broker hosts all enabled messenger instances The system SHALL let the machine-local broker own lifecycle and ingress for every enabled local messenger instance that it is allowed to operate. @@ -339,3 +350,39 @@ Broker topology SHALL keep shared-room delegation execution state local to the m - **THEN** federation messages must preserve the same task identity, peer trust, authorization, expiry, approval, and loop-prevention rules as messenger-visible coordination - **AND** they must not send bot tokens, hidden prompts, full transcripts, raw tool inputs, or file bytes unless a separate safe file-transfer capability explicitly allows it +### Requirement: Broker clients re-register routes after broker recovery +Broker clients SHALL re-register every live local session route after connecting or reconnecting to the authoritative broker for their broker scope. + +#### Scenario: Broker socket is recreated +- **WHEN** a Pi session has an in-memory route and reconnects after the broker socket is recreated +- **THEN** the client re-registers that route with the broker before reporting the route as synchronized + +#### Scenario: Multiple clients reconnect to the same recovered broker +- **WHEN** multiple Pi sessions reconnect after a broker restart or socket recovery +- **THEN** each client registers its own route with the same authoritative broker +- **AND** the broker session list contains all online routes from those clients + +#### Scenario: A replacement client registers before the previous socket closes +- **WHEN** a live session route is re-registered on a replacement client socket +- **AND** the previous socket later unregisters or disconnects +- **THEN** the broker retains the replacement socket's route registration +- **AND** the session remains online in broker-backed session lists + +#### Scenario: Re-registration contains stale binding metadata +- **WHEN** a reconnecting client re-registers a route whose binding metadata is stale relative to persisted binding authority +- **THEN** the broker applies binding authority before delivery or persisted binding updates +- **AND** stale metadata does not resurrect revoked or moved bindings + +### Requirement: Broker scope diagnostics are secret-safe +Broker startup, recovery, and singleton diagnostics SHALL identify the broker scope without exposing tokens, pairing codes, hidden prompts, tool internals, or transcripts. + +#### Scenario: Duplicate startup is prevented +- **WHEN** the supervisor prevents a concurrent broker startup for an existing scope +- **THEN** diagnostics may report the state directory category, non-secret namespace, and token hash prefix +- **AND** diagnostics MUST NOT print the bot/account token or full persisted state + +#### Scenario: Stale coordination files are cleaned +- **WHEN** stale pid, socket, or lock files are removed +- **THEN** diagnostics describe the cleanup using secret-safe path categories and broker scope labels +- **AND** persisted bindings remain intact + diff --git a/package-lock.json b/package-lock.json index 2ac2074..3d0db98 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,12 +1,12 @@ { "name": "@zylab/pirelay", - "version": "0.8.0", + "version": "0.9.0", "lockfileVersion": 3, "requires": true, "packages": { "": { "name": "@zylab/pirelay", - "version": "0.8.0", + "version": "0.9.0", "license": "MIT", "dependencies": { "@mariozechner/jiti": "^2.6.5", diff --git a/package.json b/package.json index c08252a..9377160 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "@zylab/pirelay", - "version": "0.8.0", + "version": "0.9.0", "description": "Pi package that pairs messenger chats to active Pi sessions for notifications and remote control.", "type": "module", "license": "MIT",