Skip to content
Open
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
124 changes: 124 additions & 0 deletions ASYNC_MESSAGE_FUTURE.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,124 @@
# Future: Async Message Visibility in Debug-Agent

## Current Limitation

The debug-agent (and any pi session) operates in a **synchronous turn-based model**:
- When processing a user prompt, the LLM generates a response
- During this generation, incoming `send_to_session` messages are queued
- These queued messages are **invisible** until the current turn completes
- Only then does pi process the queue and present them as new user prompts

This creates confusion in debug/observability scenarios where admins expect real-time interaction.

## Short-term Mitigation (this PR)

- Document the limitation clearly
- Add `/ready` command to signal completion
- Advise keeping responses short
- Provide socket checking workarounds

## Long-term Solutions

### Option 1: Pi Core Enhancement - Message Queue API

Extend pi's `ExtensionAPI` to expose pending message count:

```typescript
interface ExtensionAPI {
// ... existing methods

/**
* Get count of pending send_to_session messages in queue
* Returns 0 if no messages pending, N if messages queued
*/
getPendingMessageCount(): number;

/**
* Get preview of pending messages (if available)
* Useful for dashboard indicators
*/
getPendingMessagePreviews(): Array<{
from: string; // sender session ID or name
preview: string; // first 50 chars
timestamp: Date;
}>;
}
```

Then the dashboard could show:
```
┌─────────────────────────────────────────────────────┐
│ 📬 3 messages queued (complete turn to process) │
│ • control-agent: "can you check..." │
│ • user: "status update?" │
└─────────────────────────────────────────────────────┘
```

**Implementation**: Requires changes to pi's session control layer (WebSocket/message routing)

### Option 2: Streaming Response with Message Interruption

More ambitious - allow interrupting mid-generation:

```typescript
pi.on("message_received_during_turn", async (event, ctx) => {
// Opportunity to abort current generation
// Present queued message immediately
// Or queue for "after this sentence"
});
```

**Challenges**:
- Requires streaming/incremental generation support
- Complex state management (partial responses)
- May confuse conversation context

### Option 3: Parallel Session Mode

Create a "monitor" mode for debug-agent that spawns a parallel session:

```typescript
// Main session: normal turn-based interaction
// Monitor session: async-only, polls for messages every 2s

pi.registerExtension("async-monitor", {
async init(ctx) {
if (ctx.sessionRole === "debug-observer") {
setInterval(() => {
// Poll session control for pending messages
// Display in dashboard without blocking main turn
}, 2000);
}
}
});
```

**Pros**: No pi core changes needed
**Cons**: Complex dual-session architecture, more resources

### Option 4: Event-Driven Dashboard Widget

The dashboard widget already runs outside the LLM turn. Enhance it to:
1. Listen on the session socket directly (bypassing pi's queue)
2. Display pending messages in the widget itself (not as user prompts)
3. Use `/accept-message <id>` command to pull from queue into conversation

**Pros**: Clean separation of monitoring vs. conversation
**Cons**: Two different interaction modes (in-chat vs. dashboard commands)

## Recommendation

**Phase 1** (this PR): Documentation + workarounds
**Phase 2**: Implement Option 1 (message queue API in pi core)
**Phase 3**: Consider Option 4 if Option 1 proves insufficient

Option 1 is the cleanest long-term solution as it:
- Preserves pi's turn-based model
- Adds visibility without changing core behavior
- Minimal API surface area
- Useful for all session types, not just debug-agent

## Related Pi Issues

- (TODO: check if @mariozechner/pi-coding-agent has issue tracker)
- Consider proposing this enhancement upstream
11 changes: 11 additions & 0 deletions pi/skills/debug-agent/SKILL.md
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,16 @@ The activity feed tails the control-agent's session JSONL file — it updates au
- **Run diagnostics**: check bridge health, socket state, process trees
- **Make code changes**: edit extensions, skills, configs — same tools as any agent

## Known limitations

**Async message visibility**: Messages sent to this session via `send_to_session` are queued by pi but won't be visible until you complete your current response. This is a fundamental limitation of pi's turn-based conversation model.

**Workaround**: If you suspect messages are queued:
1. Keep responses short to release the turn quickly
2. Say "Ready for next message" or use `/ready` to signal completion
3. The queued messages will then appear as new user prompts
4. Check your session socket in `/session-control/` to see if connections are pending
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Inconsistent socket path (missing ~/.pi/ prefix)

Step 4 of the workaround refers to /session-control/ as an absolute path, but the Quick Reference table below shows the actual path as ~/.pi/session-control/control-agent.alias. An admin following this hint literally would look in the wrong directory.

Suggested change
4. Check your session socket in `/session-control/` to see if connections are pending
4. Check your session socket in `~/.pi/session-control/` to see if connections are pending
Prompt To Fix With AI
This is a comment left during a code review.
Path: pi/skills/debug-agent/SKILL.md
Line: 43

Comment:
**Inconsistent socket path (missing `~/.pi/` prefix)**

Step 4 of the workaround refers to `/session-control/` as an absolute path, but the Quick Reference table below shows the actual path as `~/.pi/session-control/control-agent.alias`. An admin following this hint literally would look in the wrong directory.

```suggestion
4. Check your session socket in `~/.pi/session-control/` to see if connections are pending
```

How can I resolve this? If you propose a fix, please make it concise.


## What you should NOT do

- Don't send disruptive messages to the control-agent while it's mid-task (check activity feed first)
Expand All @@ -53,3 +63,4 @@ The activity feed tails the control-agent's session JSONL file — it updates au
## Commands

- `/dashboard` — force-refresh the health metrics
- `/ready` — signal completion and allow queued messages to be processed
8 changes: 8 additions & 0 deletions pi/skills/debug-agent/debug-dashboard.ts
Original file line number Diff line number Diff line change
Expand Up @@ -807,6 +807,14 @@ export default function dashboardExtension(pi: ExtensionAPI): void {
},
});

// /ready command — signal completion for queued messages
pi.registerCommand("ready", {
description: "Signal completion to allow queued send_to_session messages to be processed",
handler: async (_args, ctx) => {
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Bug: The /ready command handler calls ctx.ui.notify() without a ctx.hasUI check, which will cause a runtime error in non-interactive sessions where no UI is present.
Severity: MEDIUM

Suggested Fix

Add a guard clause at the beginning of the /ready command's handler function. Check if ctx.hasUI is false, and if so, return early to prevent the ctx.ui.notify() call. For example: if (!ctx.hasUI) { return; }.

Prompt for AI Agent
Review the code at the location below. A potential bug has been identified by an AI
agent.
Verify if this is a real issue. If it is, propose a fix; if not, explain why it's not
valid.

Location: pi/skills/debug-agent/debug-dashboard.ts#L813

Potential issue: The handler for the new `/ready` command calls `ctx.ui.notify()`
without first verifying the presence of a user interface via the `ctx.hasUI` flag. In
non-interactive environments, such as headless or SDK modes, the `ctx.ui` object is not
available. Attempting to call `notify()` on an undefined `ui` object will result in a
runtime error, causing the command to crash. This is inconsistent with the established
pattern in the codebase where other command handlers perform this check before
interacting with the UI.

Did we get this right? 👍 / 👎 to inform future reviews.

ctx.ui.notify("Ready for next message. Complete your response to process queue.", "info");
},
});
Comment on lines +810 to +816
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

/ready doesn't actually signal turn completion

The handler only fires a UI notification — it doesn't terminate the agent's current response turn or trigger queue processing in any way. Both the command description and the SKILL.md documentation claim it will "signal completion to allow queued send_to_session messages to be processed," but a ctx.ui.notify call has no effect on turn state.

An admin who types /ready expecting queued messages to surface will see the notification and believe the workaround worked, when in fact nothing has changed: the agent turn is still in flight and the queue is still blocked. This makes the command actively misleading rather than helpful.

If there is a way to end the current turn early (e.g. a ctx.session.complete(), returning a value from the handler that pi interprets as a final response, or emitting a turn-end event), that mechanism should be used here. If no such API exists, the command and its documentation should be rephrased to clarify that it only sends a reminder notification — it does not release the queue — and the admin still has to wait for the agent's natural turn completion.

Prompt To Fix With AI
This is a comment left during a code review.
Path: pi/skills/debug-agent/debug-dashboard.ts
Line: 810-816

Comment:
**`/ready` doesn't actually signal turn completion**

The handler only fires a UI notification — it doesn't terminate the agent's current response turn or trigger queue processing in any way. Both the command `description` and the `SKILL.md` documentation claim it will *"signal completion to allow queued send_to_session messages to be processed,"* but a `ctx.ui.notify` call has no effect on turn state.

An admin who types `/ready` expecting queued messages to surface will see the notification and believe the workaround worked, when in fact nothing has changed: the agent turn is still in flight and the queue is still blocked. This makes the command actively misleading rather than helpful.

If there is a way to end the current turn early (e.g. a `ctx.session.complete()`, returning a value from the handler that pi interprets as a final response, or emitting a turn-end event), that mechanism should be used here. If no such API exists, the command and its documentation should be rephrased to clarify that it only sends a reminder notification — it does **not** release the queue — and the admin still has to wait for the agent's natural turn completion.

How can I resolve this? If you propose a fix, please make it concise.


pi.on("session_start", async (_event, ctx) => {
savedCtx = ctx;
await refresh();
Expand Down