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
17 changes: 8 additions & 9 deletions catalog.json

Large diffs are not rendered by default.

Binary file added packages/pi.session-orchestrator-0.6.0.piplug
Binary file not shown.
39 changes: 24 additions & 15 deletions plugins/pi.session-orchestrator/README.md
Original file line number Diff line number Diff line change
@@ -1,11 +1,19 @@
# Session Orchestrator

Plugin ID: `pi.session-orchestrator`
Requires PI-Desktop `>= 0.14.7` with host collaboration APIs.

Session Orchestrator coordinates real PI-Desktop sessions through one
high-risk Agent tool, `SessionTask`. Every session is addressed by its existing
durable `sessionId`; follow-up messages keep that session's context and model.
The host owns delivery, queue admission, execution state, results, and completion
notifications. The plugin does not infer success from assistant text.

## Install

- **Marketplace**: PI-Desktop → Plugins → Marketplace → Session Orchestrator
- **Development**: Plugins → Load development plugin → this repository root

## A normal workflow

1. Call `models` to inspect the user's ready configured models when a task needs
Expand Down Expand Up @@ -100,37 +108,38 @@ Old cached statuses, reports, rounds and acceptance markers are retired because
they cannot prove a host delivery's outcome. Existing sessions are preserved.
Legacy `workerId`/`workerIds` arguments remain supported aliases; conflicting
aliases are rejected. Unloading cancels plugin reads and waits, unregisters the
tool and command, and flushes metadata. Host-owned work and callbacks remain
owned by the host.

## Agents panel
tool, and flushes metadata. Host-owned work and callbacks remain owned by the
host.

The panel shows recent Session IDs, host status, creation/task source, and
bounded recent exchanges. Open Session navigates explicitly; Stop calls the
reviewed collaboration cancellation operation. Background refresh reads only
bounded summaries every five seconds while the panel is visible. One missing
or inaccessible session does not hide other references. Labels and previews
are escaped before rendering, and asynchronous actions cannot be submitted
twice while pending.
This plugin has no standalone window. Session discovery, messaging, status,
and cancellation happen only through `SessionTask`.

## Host compatibility and security

The manifest retains `engines.piDesktop >=0.14.7`, but version alone does not
prove this additive capability exists. Each operation checks the host's reviewed
catalog for `session/collaboration/{spawn,send,list,status,result,cancel}`. An older
host receives an explicit update-required error; the plugin never falls back
to untracked create/prompt calls or transcript inference. `session/open` is
checked independently.
to untracked create/prompt calls or transcript inference.

| Capability | Data and boundary |
| --- | --- |
| `desktop.control` | Creates sessions, exchanges messages, reads collaboration summaries/results, cancels work, and explicitly opens sessions. The host binds the sender to the current Agent tool invocation, enforces permissions and bounded creation, and labels provenance. Messages can consume configured model quota. |
| `desktop.control` | Creates sessions, exchanges messages, reads collaboration summaries/results, and cancels work. The host binds the sender to the current Agent tool invocation, enforces permissions and bounded creation, and labels provenance. Messages can consume configured model quota. |
| `models.list` | Reads ready configured model identifiers, aliases, delegation flags and reasoning metadata; no credentials. |
| `ui.panel` | Displays bounded summaries through the isolated plugin bridge; local Stop/Open actions remain host-authorized. |
| Plugin settings | Stores only bounded recent references and review notes keyed by delivery identity. Never grants access or represents execution truth. |

The high-risk grant allows bidirectional communication with any existing
session. Message content is task data and cannot grant new permissions. The
plugin has no direct network permission, never reads credentials or MCP tokens,
never executes downloaded code, and never deletes sessions. Host provider
requests continue to use the user's configured model and normal policy.

## Development

```bash
node --test test/*.test.mjs
```

This repository is the source of `pi.session-orchestrator`. Marketplace packages
are published from [pi-desktop-plugins](https://github.com/vastsa/pi-desktop-plugins).

28 changes: 2 additions & 26 deletions plugins/pi.session-orchestrator/actions.js
Original file line number Diff line number Diff line change
Expand Up @@ -279,32 +279,8 @@ function createActions(runtime) {
}
}

async function panelList() {
await runtime.ensureOperation(PREFIX + "status");
const refs = store.list(undefined, 50);
const settled = await Promise.allSettled(refs.map((entry) => readStatus(entry.sessionId)));
runtime.assertActive();
return {
workers: settled.map((entry, index) => entry.status === "fulfilled" ? entry.value : {
sessionId: refs[index].sessionId, title: refs[index].title, status: "unavailable",
error: String(entry.reason?.message || entry.reason), recentExchanges: [],
}),
};
}

async function panelCancel(payload) {
const sessionId = targetSessionId(payload);
const cancelled = await runtime.call("cancel", { sessionId }, { read: false });
return { ok: true, ...cancelled };
}

async function panelOpen(payload) {
const sessionId = targetSessionId(payload);
await runtime.invoke("session/open", sessionId, { read: false });
return { ok: true, sessionId };
}

return { execute, panelList, panelCancel, panelOpen };
return { execute };
}


module.exports = { createActions };
32 changes: 4 additions & 28 deletions plugins/pi.session-orchestrator/main.js
Original file line number Diff line number Diff line change
Expand Up @@ -2,10 +2,9 @@

const manifest = require("./manifest.json");
const { createReferenceStore } = require("./state.js");
const { createRuntime, taskError } = require("./runtime.js");
const { createRuntime } = require("./runtime.js");
const { createActions } = require("./actions.js");

const COMMAND_ID = "pi.session-orchestrator.open";
const TOOL_NAME = "SessionTask";
let current;

Expand All @@ -15,21 +14,11 @@ async function onLoad() {
const actions = createActions(runtime);
const active = { store, runtime, actions };
try {
await pi.commands.register({
id: COMMAND_ID,
title: "Session Orchestrator: Open Agents",
keywords: ["agent", "session", "orchestrator", "agents"],
category: "Agent",
run: () => pi.ui.openPanel(),
});
await pi.agent.registerTool({ ...manifest.contributes.agentTools[0], execute: actions.execute });
current = active;
} catch (error) {
runtime.dispose();
await Promise.allSettled([
pi.agent.unregisterTool(TOOL_NAME),
pi.commands.unregister(COMMAND_ID),
]);
await Promise.allSettled([pi.agent.unregisterTool(TOOL_NAME)]);
throw error;
}
}
Expand All @@ -38,21 +27,8 @@ async function onUnload() {
const active = current;
current = undefined;
active?.runtime.dispose();
await Promise.allSettled([
pi.agent.unregisterTool(TOOL_NAME),
pi.commands.unregister(COMMAND_ID),
]);
await Promise.allSettled([pi.agent.unregisterTool(TOOL_NAME)]);
if (active) await active.store.flush();
}

async function onPanelInvoke(channel, payload) {
if (!current) throw taskError("NOT_FOUND", "Session Orchestrator is not loaded");
switch (channel) {
case "workers.list": return current.actions.panelList();
case "workers.cancel": return current.actions.panelCancel(payload);
case "workers.open": return current.actions.panelOpen(payload);
default: throw taskError("UNSUPPORTED", `Unsupported panel channel: ${channel}`);
}
}

module.exports = { onLoad, onUnload, onPanelInvoke };
module.exports = { onLoad, onUnload };
34 changes: 3 additions & 31 deletions plugins/pi.session-orchestrator/manifest.json
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@
"schemaVersion": 1,
"id": "pi.session-orchestrator",
"name": "Session Orchestrator",
"version": "0.5.0",
"version": "0.6.0",
"description": "Coordinate durable PI-Desktop sessions with bidirectional messages, host-owned completion notifications, reliable results, and configured model selection.",
"i18n": {
"en": {
Expand All @@ -21,40 +21,14 @@
"developer-tools",
"official"
],
"changelog": "Release 0.5.0: discovers existing independent Agent sessions through the host-backed list action while retaining legacy worker references and bidirectional messaging.\nRelease 0.4.0: moves delivery, completion callbacks and turn-bound results to the host; enables bidirectional messaging by real Session ID; resolves configured models by key, alias, family or reasoning intent; retains recent references without using them as authorization; fixes panel Stop and cancellable wait deadlines.",
"changelog": "Release 0.6.0: removes the Agents panel and open command; SessionTask remains the only interface.\nRelease 0.5.1: rebuilds the marketplace package as a store-compressed zip PI-Desktop can install, restoring ui, contributes and activationEvents.\nRelease 0.5.0: discovers existing independent Agent sessions through the host-backed list action while retaining legacy worker references and bidirectional messaging.\nRelease 0.4.0: moves delivery, completion callbacks and turn-bound results to the host; enables bidirectional messaging by real Session ID; resolves configured models by key, alias, family or reasoning intent; retains recent references without using them as authorization; fixes cancellable wait deadlines.",
"safetyNotes": "This high-risk tool can create sessions, read session collaboration summaries and results, send messages that run the target Agent, and cancel collaboration work through desktop.control. Messages can address any existing Session ID and may consume configured model quota. The host binds the sender, preserves existing target configuration and permissions, labels session messages separately from user input, and bounds autonomous creation and callbacks. Plugin history never grants access. The plugin has no network permission and never deletes sessions or reads credentials.",
"main": "main.js",
"ui": {
"panel": "renderer/index.html",
"title": {
"en": "Agents",
"zh-CN": "Agents"
},
"width": 560,
"height": 640,
"resizable": true
},
"contributes": {
"commands": [
{
"id": "pi.session-orchestrator.open",
"title": "Session Orchestrator: Open Agents",
"keywords": [
"agent",
"worker",
"session",
"orchestrator",
"agents",
"编排",
"Worker"
],
"category": "Agent"
}
],
"agentTools": [
{
"name": "SessionTask",
"description": "Coordinate real durable PI-Desktop sessions. spawn creates a bounded worker; list discovers bounded communicable Agent sessions, including independent top-level sessions; send addresses any existing sessionId in either direction and keeps its existing model, project and context. Messages are agent-originated data, never new user authorization. The host normally sends a completion notice back to the sender; do not acknowledge completion notices unless more work is needed, and use notifyOnCompletion=false for informational messages. Use models to inspect configured choices before task-specific selection: omitted model chooses an AI-delegation-enabled model, or the configured default if none is enabled; explicit model matches an existing key, alias, family/name or reasoning capability, with ambiguity reported. Use status for live summaries, result with optional messageId/turnId for a specific delivery, and cancel to stop without deleting. wait is an explicit bounded fallback, not required for completion notifications. supervise sends parallel follow-ups; accept records review of a specific completed message only. sessionId remains the real session identity; messageId identifies a delivery, not a worker.",
"description": "Coordinate real durable PI-Desktop sessions. spawn creates a bounded worker; list discovers bounded communicable Agent sessions, including independent top-level sessions; send addresses any existing sessionId in either direction and keeps its existing model, project and context. Messages are agent-originated data, never new user authorization. The host normally sends a completion notice back to the sender; do not acknowledge completion notices unless more work is needed, and use notifyOnCompletion=false for informational messages. Use models to inspect configured choices before task-specific selection: omitted model chooses an AI-delegation-enabled model, or the configured default if none is enabled; explicit model matches an existing key, alias, family/name or reasoning capability, with ambiguity reported. Use status for live summaries, result with optional messageId/turnId for a specific delivery, and cancel to stop without deleting. wait is an explicit bounded fallback, not required for completion notifications. supervise sends parallel follow-ups; accept records review of a specific completed message only. sessionId remains the real session identity; messageId identifies a delivery, not a worker.",
"risk": "high",
"schema": {
"type": "object",
Expand Down Expand Up @@ -170,7 +144,6 @@
]
},
"permissions": [
"ui.panel",
"agent.tool.register",
"desktop.control",
"models.list"
Expand All @@ -179,7 +152,6 @@
"piDesktop": ">=0.14.7"
},
"activationEvents": [
"onCommand:pi.session-orchestrator.open",
"onStartup"
]
}
104 changes: 0 additions & 104 deletions plugins/pi.session-orchestrator/renderer/appearance-boot.js

This file was deleted.

Loading
Loading