Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
10 changes: 10 additions & 0 deletions AGENTS.md
Original file line number Diff line number Diff line change
Expand Up @@ -48,6 +48,8 @@ The legacy Telegram-tunnel extension path has been removed; do not add compatibi
- Add unit tests for pure helpers.
- Add runtime/integration tests for authorization, prompt routing, persisted state, media handling, and broker parity.
- For Telegram UX changes, cover both happy path and ambiguous/error states.
- For event-driven changes, define lifecycle states and safety invariants before implementation. Cover identifiers present and missing, repeated calls, partial or delayed events, authorization denial, and delivery failure where applicable.
- When multiple handlers participate in one lifecycle, test complete event sequences and assert that fallback paths preserve the same authorization, redaction, correlation, and deduplication invariants.
- Prefer small targeted tests over brittle transcript snapshots.

## OpenSpec workflow
Expand All @@ -60,6 +62,14 @@ When implementing an OpenSpec change:
4. Validate the change with `openspec validate <change> --strict`.
5. Archive only after implementation is complete and specs are synced.

## Review feedback workflow

1. Verify each comment against the current code before changing it.
2. When feedback exposes a shared invariant or lifecycle flaw, audit adjacent handlers and equivalent paths instead of patching only the cited line.
3. Add focused regression tests plus a complete lifecycle test for systemic fixes.
4. Batch related feedback, run the required checks, and perform a structured pre-review before requesting another review.
5. Re-fetch unresolved threads after pushing. Avoid repeated automated review requests after individual micro-fixes; request review once the related batch is complete and validated.

## Git workflow

- Use concise Conventional Commits-style messages.
Expand Down
2 changes: 1 addition & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -274,7 +274,7 @@ Then invite the app to the channel and pair in that channel/thread with `relay p
| create delegation task (opt-in shared rooms) | `/delegate <machine\|#capability> <goal>` | `relay delegate <machine\|#capability> <goal>` | `/relay delegate <machine\|#capability> <goal>` |
| control delegation task | `/task@<bot_username> <claim\|decline\|cancel\|status\|history> [task-id]` (or `/task <claim\|decline\|cancel\|status\|history> [task-id]` in private/other clients) | `relay task <claim\|decline\|cancel\|status\|history> [task-id]` | `/relay task <claim\|decline\|cancel\|status\|history> [task-id]` |

`quiet`, `normal`, `verbose`, and `completion-only` are valid progress modes. Progress mode controls non-terminal progress noise: quiet suppresses progress updates, completion-only sends final results plus safe compaction notifications, normal sends coalesced milestone progress, and verbose additionally includes safe visible model/tool snapshots at a shorter interval. Progress updates are deduplicated/coalesced, and supported messengers update the same live progress message in place where possible instead of posting every raw Pi stream event. Terminal notifications still deliver the final assistant answer when it fits safe platform limits, splitting by paragraphs within platform limits and falling back to a Markdown document when an adapter supports files and the output is too large for a reasonable chat burst.
`quiet`, `normal`, `verbose`, and `completion-only` are valid progress modes. Progress mode controls non-terminal progress noise: quiet suppresses progress updates, completion-only sends final results plus safe compaction notifications, normal sends coalesced milestone progress, and verbose additionally includes safe visible model/tool snapshots at a shorter interval. Tool progress in normal/verbose modes is summarized as a bounded live card with safe intent labels such as `▶ bash: npm test`, `✓ read: extensions/relay/runtime/extension-runtime.ts`, or `✕ rg: pattern in extensions`, plus aggregate counts like `tools: bash×2 read×4`; it never includes tool output, file contents, replacement text, raw transcripts, or arbitrary custom-tool arguments. Progress updates are deduplicated/coalesced, and supported messengers update the same live progress message in place where possible instead of posting every raw Pi stream event. Terminal notifications still deliver the final assistant answer when it fits safe platform limits, splitting by paragraphs within platform limits and falling back to a Markdown document when an adapter supports files and the output is too large for a reasonable chat burst.

Remote `/disconnect` is scoped to the requesting chat/conversation only: it revokes that Telegram, Discord, or Slack binding and suppresses future session output/buttons there, without disconnecting other messengers that remain paired to the same Pi session. Local `/relay disconnect` is broader and disconnects the current session from all paired messenger bindings.

Expand Down
2 changes: 1 addition & 1 deletion extensions/relay/broker/process.js
Original file line number Diff line number Diff line change
Expand Up @@ -1245,7 +1245,7 @@ function syncProgressDelivery(route) {
if (state.timer) return;
const interval = progressIntervalMsFor(mode, config);
const elapsed = state.lastSentAt ? Date.now() - state.lastSentAt : interval;
const delay = Math.max(0, interval - elapsed);
const delay = Math.max(1, interval - elapsed);
state.timer = setTimeout(() => {
void flushProgress(route.sessionKey, route.binding.chatId, route.binding.userId, key);
}, delay);
Expand Down
10 changes: 8 additions & 2 deletions extensions/relay/notifications/progress.ts
Original file line number Diff line number Diff line change
Expand Up @@ -142,7 +142,13 @@ export function coalesceLiveProgressEntries(entries: ProgressActivityEntry[]): P
const existing = milestones.get(key);
if (existing) {
existing.count = (existing.count ?? 1) + 1;
existing.at = Math.max(existing.at, entry.at);
if (entry.at >= existing.at) {
existing.text = entry.text;
existing.detail = entry.detail;
existing.delivery = entry.delivery;
existing.semanticKey = entry.semanticKey;
existing.at = entry.at;
}
continue;
}
milestones.set(key, { ...entry });
Expand All @@ -152,7 +158,7 @@ export function coalesceLiveProgressEntries(entries: ProgressActivityEntry[]): P
return ([...milestones.values(), ...latestVolatile] as CountedProgressActivityEntry[])
.sort((left, right) => left.at - right.at)
.slice(-5)
.map((entry) => entry.count && entry.count > 1 ? { ...entry, text: `${entry.text} (${entry.count}×)` } : entry);
.map((entry) => entry.count && entry.count > 1 && entry.semanticKey !== "tool-progress" ? { ...entry, text: `${entry.text} (${entry.count}×)` } : entry);
}

export function formatProgressUpdate(entries: ProgressActivityEntry[], config: Pick<TelegramTunnelConfig, "maxProgressMessageChars">, options: { header?: boolean; marker?: string } = {}): string | undefined {
Expand Down
Loading