pi-session-orchestrator 0.2.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE +21 -0
- package/README.md +307 -0
- package/assets/pi-session-orchestrator-thumbnail-pi-sessions.png +0 -0
- package/docs/README.md +18 -0
- package/docs/pi-intercom.md +7 -0
- package/docs/prompt-injection.md +52 -0
- package/docs/reference/README.md +14 -0
- package/docs/reference/coordination-model.md +9 -0
- package/docs/reference/fast-decision-model.md +23 -0
- package/docs/reference/operational-boundaries.md +7 -0
- package/docs/release-version-preparation.md +55 -0
- package/package.json +58 -0
- package/src/binding.ts +50 -0
- package/src/index.ts +311 -0
- package/src/model.ts +164 -0
- package/src/prompt.ts +118 -0
- package/src/state.ts +639 -0
- package/src/tools.ts +275 -0
package/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 David Jadczyk
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
package/README.md
ADDED
|
@@ -0,0 +1,307 @@
|
|
|
1
|
+
<p align="center">
|
|
2
|
+
<img src="https://img.shields.io/badge/Pi-extension-0ea5e9?style=flat-square" alt="Pi extension" />
|
|
3
|
+
<img src="https://img.shields.io/badge/coordination-explicit-18181b?style=flat-square" alt="Explicit coordination" />
|
|
4
|
+
<img src="https://img.shields.io/badge/session%20depth-max%202-22c55e?style=flat-square" alt="Maximum session depth two" />
|
|
5
|
+
</p>
|
|
6
|
+
|
|
7
|
+
<p align="center">
|
|
8
|
+
<img src="./assets/pi-session-orchestrator-thumbnail-pi-sessions.png" width="720" alt="Pi sessions coordinated through a root and domain hierarchy" />
|
|
9
|
+
</p>
|
|
10
|
+
|
|
11
|
+
<h1 align="center">Pi Session Orchestrator</h1>
|
|
12
|
+
|
|
13
|
+
<p align="center">
|
|
14
|
+
Durable, explicit coordination for independently running Pi sessions.<br />
|
|
15
|
+
Keep focused work focused while preserving ownership, context boundaries, and human authority.
|
|
16
|
+
</p>
|
|
17
|
+
|
|
18
|
+
<p align="center">
|
|
19
|
+
<a href="#why">Why</a> ·
|
|
20
|
+
<a href="#coordination-model">Coordination model</a> ·
|
|
21
|
+
<a href="#use">Use</a> ·
|
|
22
|
+
<a href="#safety-boundaries">Safety</a> ·
|
|
23
|
+
<a href="docs/README.md">Documentation</a> ·
|
|
24
|
+
<a href="https://github.com/davidjadczyk/pi-session-orchestrator/blob/main/CONTRIBUTING.md">Contributing</a> ·
|
|
25
|
+
<a href="https://github.com/davidjadczyk/pi-session-orchestrator/blob/main/CODE_OF_CONDUCT.md">Code of Conduct</a> ·
|
|
26
|
+
<a href="docs/release-version-preparation.md">Release preparation</a>
|
|
27
|
+
</p>
|
|
28
|
+
|
|
29
|
+
---
|
|
30
|
+
|
|
31
|
+
`pi-session-orchestrator` is a local coordination layer for existing Pi sessions. It records explicit assignments, injects concise role context, and keeps handoffs inspectable across reloads and resumes.
|
|
32
|
+
|
|
33
|
+
It does **not** spawn sessions, create worktrees, merge changes, or replace Pi, Gentle Pi, OpenSpec/SDD, Orca, `pi-subagents`, or [`pi-intercom`](./docs/pi-intercom.md). `pi-intercom` is an optional companion transport, not a package dependency.
|
|
34
|
+
|
|
35
|
+
## Why
|
|
36
|
+
|
|
37
|
+
Large work often needs two things that conflict:
|
|
38
|
+
|
|
39
|
+
- a session that retains the broad view across repositories, features, and worktrees;
|
|
40
|
+
- sessions that stay concentrated on one bounded outcome.
|
|
41
|
+
|
|
42
|
+
This extension separates those responsibilities without inventing authority. Every managed relationship is explicit, durable, and bound to a canonical repository/worktree identity.
|
|
43
|
+
|
|
44
|
+
| Without coordination state | With `pi-session-orchestrator` |
|
|
45
|
+
| --- | --- |
|
|
46
|
+
| A session tree or working directory implies a relationship. | Assignment, role, scope, and binding are recorded explicitly. |
|
|
47
|
+
| Focused work accumulates cross-cutting context. | Coordinators route cross-boundary decisions; focused sessions retain narrow context. |
|
|
48
|
+
| “Done” can mean an unowned result. | A handoff is durable evidence awaiting explicit acceptance or return. |
|
|
49
|
+
| A footer can be mistaken for authority. | The footer is display-only; persisted assignment state remains authoritative. |
|
|
50
|
+
|
|
51
|
+
## Coordination model
|
|
52
|
+
|
|
53
|
+
The hierarchy is deliberately small and explicit. Git repository and worktree topology limits *where* a session may operate; it never infers *who* coordinates whom.
|
|
54
|
+
|
|
55
|
+
```mermaid
|
|
56
|
+
flowchart TD
|
|
57
|
+
root["🧭 Root coordinator<br/>Cross-repository and integration decisions"]
|
|
58
|
+
direct["🎯 Focused session<br/>Direct bounded work"]
|
|
59
|
+
domain["🧭 Domain coordinator<br/>Delegated repository or subsystem"]
|
|
60
|
+
coreA["🎯 Focused session<br/>Bounded Core work"]
|
|
61
|
+
coreB["🎯 Focused session<br/>Tests or verification"]
|
|
62
|
+
human(["Human authority<br/>Product, cost, security, destructive decisions"])
|
|
63
|
+
|
|
64
|
+
root -->|direct assignment| direct
|
|
65
|
+
root -->|delegated boundary| domain
|
|
66
|
+
domain -->|focused assignment| coreA
|
|
67
|
+
domain -->|focused assignment| coreB
|
|
68
|
+
root -. consequential decision .-> human
|
|
69
|
+
domain -. cross-boundary decision .-> root
|
|
70
|
+
direct -. local blocker or decision .-> root
|
|
71
|
+
coreA -. local blocker or decision .-> domain
|
|
72
|
+
|
|
73
|
+
classDef coordinator fill:#0e7490,color:#fff,stroke:#155e75;
|
|
74
|
+
classDef focused fill:#334155,color:#fff,stroke:#1e293b;
|
|
75
|
+
classDef human fill:#4d7c0f,color:#fff,stroke:#3f6212;
|
|
76
|
+
class root,domain coordinator;
|
|
77
|
+
class direct,coreA,coreB focused;
|
|
78
|
+
class human human;
|
|
79
|
+
```
|
|
80
|
+
|
|
81
|
+
| Role | Owns | May create managed children |
|
|
82
|
+
| --- | --- | --- |
|
|
83
|
+
| **Root coordinator** | Cross-feature, cross-repository, and integration decisions. | Domain coordinators or direct focused sessions. |
|
|
84
|
+
| **Domain coordinator** | A delegated repository or subsystem boundary. | Focused sessions only. |
|
|
85
|
+
| **Focused session** | One bounded objective, its evidence, and its handoff. | None. |
|
|
86
|
+
| **Unmanaged session** | Normal Pi behavior. | Outside this extension. |
|
|
87
|
+
|
|
88
|
+
### Hard limits
|
|
89
|
+
|
|
90
|
+
```text
|
|
91
|
+
root coordinator → domain coordinator → focused session
|
|
92
|
+
```
|
|
93
|
+
|
|
94
|
+
- The maximum managed depth is two edges.
|
|
95
|
+
- Root coordinators may assign focused sessions directly.
|
|
96
|
+
- Domain coordinators cannot create another domain coordinator.
|
|
97
|
+
- Focused sessions cannot create managed children.
|
|
98
|
+
- Every relationship is explicitly attached and validated against the active repository/worktree binding.
|
|
99
|
+
|
|
100
|
+
## What it supports
|
|
101
|
+
|
|
102
|
+
| Capability | What it provides |
|
|
103
|
+
| --- | --- |
|
|
104
|
+
| **Explicit assignment** | Durable coordinator, target-session, role, objective, allowed-scope, and canonical-binding records. |
|
|
105
|
+
| **Explicit opt-in delegation baseline** | A concise named additive prompt section permits small known work inline; requires an explicit durable assignment for orchestration/delegation; and directs oversized work to fast focused delegation. |
|
|
106
|
+
| **Role-aware context** | A separate concise named role section appears after an explicit durable transition. |
|
|
107
|
+
| **Bounded hierarchy** | Root, domain, and focused roles with a hard depth limit. |
|
|
108
|
+
| **Durable lifecycle** | Assignment state and handoff evidence survive reload and resume. |
|
|
109
|
+
| **Decision routing** | Typed progress, blocker, decision-request, handoff, and review-result records. |
|
|
110
|
+
| **Display-only status** | Compact footer status derived from persisted state, never from UI state or inference. |
|
|
111
|
+
| **Unmanaged fallback** | Sessions without a valid explicit attachment receive only the universal baseline, no role context, and no footer. |
|
|
112
|
+
|
|
113
|
+
### What it deliberately does not do
|
|
114
|
+
|
|
115
|
+
- Infer relationships from conversation text, a session tree, a repository, or a worktree.
|
|
116
|
+
- Replace `pi-intercom` transport, generic messaging, or Pi subagent supervision.
|
|
117
|
+
- Transfer RDD lineages between sessions.
|
|
118
|
+
- Authorize edits, commits, pushes, reviews, merges, deployments, or cleanup.
|
|
119
|
+
- Create processes, sessions, branches, or worktrees automatically.
|
|
120
|
+
- Support managed hierarchies deeper than root → domain → focused.
|
|
121
|
+
|
|
122
|
+
## Install and discover
|
|
123
|
+
|
|
124
|
+
The published package is discoverable in the Pi package gallery through the `pi-package` keyword. Its `pi.extensions` manifest points Pi at `./src/index.ts`, while the gallery image is hosted from the public `main` branch.
|
|
125
|
+
|
|
126
|
+
After a release, install it in Pi with:
|
|
127
|
+
|
|
128
|
+
```text
|
|
129
|
+
pi install npm:pi-session-orchestrator
|
|
130
|
+
```
|
|
131
|
+
|
|
132
|
+
For local development, install dependencies and run the extension through Pi from this repository:
|
|
133
|
+
|
|
134
|
+
```bash
|
|
135
|
+
npm install
|
|
136
|
+
npm test
|
|
137
|
+
```
|
|
138
|
+
|
|
139
|
+
Release preparation and the protected-branch publishing procedure are documented in [Release and version preparation](docs/release-version-preparation.md).
|
|
140
|
+
|
|
141
|
+
### Agent control plane
|
|
142
|
+
|
|
143
|
+
Agents use two narrow tools:
|
|
144
|
+
|
|
145
|
+
| Tool | Purpose |
|
|
146
|
+
| --- | --- |
|
|
147
|
+
| `orchestrator_status` | Read the current role, footer, pending attachment, direct assignments, and valid next context. |
|
|
148
|
+
| `orchestrator_update` | Explicitly register, adopt existing sessions, attach, transition lifecycle, request a decision, submit a handoff, settle, or end an assignment. |
|
|
149
|
+
| `orchestrator_ledger` | Read a bounded per-root ledger of typed assignments, events, handoffs, and presence for explicit analysis. |
|
|
150
|
+
|
|
151
|
+
The universal baseline permits small, known work to remain inline. It never infers an orchestrator from a session creation, conversation, repository, or worktree; orchestration or delegation requires an explicit durable assignment. A successful update returns the state delta immediately; role-specific prompt context applies on the next model run.
|
|
152
|
+
|
|
153
|
+
### User-visible orchestration events
|
|
154
|
+
|
|
155
|
+
The extension projects only explicit durable mutations as subtle UI event cards: coordinator registration, assignment lifecycle changes, handoffs, blockers, decision requests, and effective presence transitions. Repeated unchanged presence observations do not create events. Cards are custom session entries excluded from LLM context; when that renderer is unavailable, Pi's widget/notification UI is used without injecting event prose into prompts. The footer remains assignment-derived, so a registered coordinator with no matching assignment still shows `Root · 0 children`.
|
|
156
|
+
|
|
157
|
+
If the actual situation is an existing focused session that has not been assigned, repair it from the coordinator session instead of relying on a session tree or prompt text:
|
|
158
|
+
|
|
159
|
+
```text
|
|
160
|
+
orchestrator_update({
|
|
161
|
+
operation: "assign-existing",
|
|
162
|
+
assignments: [{ sessionId: "<focused-session-uuid>", role: "focused-session", objective: "<bounded objective>", allowedScope: "<allowed scope>" }]
|
|
163
|
+
})
|
|
164
|
+
```
|
|
165
|
+
|
|
166
|
+
Then, in the focused session, validate its binding and run `orchestrator_update({ operation: "attach", assignmentId: "<assignment-id>" })`, followed by `orchestrator_update({ operation: "start" })` when work begins. No generic Pi subagent, process, session, repository, worktree, or prompt observation creates an orchestration event or relationship.
|
|
167
|
+
|
|
168
|
+
### 1. Register a root coordinator
|
|
169
|
+
|
|
170
|
+
An agent records the current session deliberately:
|
|
171
|
+
|
|
172
|
+
```text
|
|
173
|
+
orchestrator_update({ operation: "register-root" })
|
|
174
|
+
```
|
|
175
|
+
|
|
176
|
+
The equivalent human recovery command remains available:
|
|
177
|
+
|
|
178
|
+
```text
|
|
179
|
+
/orchestrator coordinator
|
|
180
|
+
```
|
|
181
|
+
|
|
182
|
+
### 2. Adopt existing sessions explicitly
|
|
183
|
+
|
|
184
|
+
After Pi, Orca, or a human creates a session through its normal mechanism, a root can adopt one or more known session IDs in one atomic update. Both `orchestrator_update({ operation: "assign-existing" })` and `/orchestrator assign` require a full canonical Pi session UUID; abbreviated display IDs are rejected. Optional `worktree` values bind each target to its own canonical Git worktree.
|
|
185
|
+
|
|
186
|
+
```text
|
|
187
|
+
orchestrator_update({
|
|
188
|
+
operation: "assign-existing",
|
|
189
|
+
assignments: [
|
|
190
|
+
{ sessionId: "11111111-1111-4111-8111-111111111111", role: "focused-session", objective: "Implement the bounded UI change", allowedScope: "src/ui/**", worktree: "/repo/ui" },
|
|
191
|
+
{ sessionId: "22222222-2222-4222-8222-222222222222", role: "domain-coordinator", objective: "Coordinate Core migration", allowedScope: "core/**", worktree: "/repo/core" }
|
|
192
|
+
]
|
|
193
|
+
})
|
|
194
|
+
```
|
|
195
|
+
|
|
196
|
+
Each target remains unmanaged until it validates and accepts its own assignment:
|
|
197
|
+
|
|
198
|
+
```text
|
|
199
|
+
orchestrator_status()
|
|
200
|
+
orchestrator_update({ operation: "attach", assignmentId: "<assignment-id>" })
|
|
201
|
+
orchestrator_update({ operation: "start" })
|
|
202
|
+
```
|
|
203
|
+
|
|
204
|
+
A domain coordinator registers itself after attachment, then may adopt focused sessions only. The human `/orchestrator assign` and `/orchestrator attach` commands remain available for manual bootstrap and recovery.
|
|
205
|
+
|
|
206
|
+
### 3. Work, hand off, and settle explicitly
|
|
207
|
+
|
|
208
|
+
Focused work starts and submits its bounded outcome:
|
|
209
|
+
|
|
210
|
+
```text
|
|
211
|
+
/orchestrator start
|
|
212
|
+
/orchestrator handoff {"outcome":"Implemented validation","changedSurfaces":["src/validation.ts"],"checks":["npm test"],"risks":[]}
|
|
213
|
+
```
|
|
214
|
+
|
|
215
|
+
The coordinating session explicitly accepts or returns the handoff:
|
|
216
|
+
|
|
217
|
+
```text
|
|
218
|
+
/orchestrator accept <assignment-id>
|
|
219
|
+
/orchestrator return <assignment-id> <reason>
|
|
220
|
+
```
|
|
221
|
+
|
|
222
|
+
A handoff is coordination evidence only. It never authorizes delivery.
|
|
223
|
+
|
|
224
|
+
## Decision routing
|
|
225
|
+
|
|
226
|
+
Do not use the parent as a progress relay. Focused sessions should work independently until a meaningful boundary is reached.
|
|
227
|
+
|
|
228
|
+
| Situation | Route |
|
|
229
|
+
| --- | --- |
|
|
230
|
+
| Routine implementation choice inside the assigned scope | Decide locally using repository evidence and governing instructions. |
|
|
231
|
+
| Local blocker, dependency, scope conflict, or sibling coordination | Ask the immediate coordinator. |
|
|
232
|
+
| Cross-feature, cross-repository, or integration decision | Route to the root coordinator. |
|
|
233
|
+
| Product priority, cost, security, secret, destructive, or other human-authority decision | Ask the human directly when governing policy requires it. |
|
|
234
|
+
| Coordinator unavailable and waiting creates material risk | Escalate directly to the human with the blocked action and evidence. |
|
|
235
|
+
|
|
236
|
+
[`pi-intercom`](./docs/pi-intercom.md) can remain the optional live transport. This extension owns the typed orchestration record, not a second messaging system, and does not depend on `pi-intercom`.
|
|
237
|
+
|
|
238
|
+
## Lifecycle and handoffs
|
|
239
|
+
|
|
240
|
+
```mermaid
|
|
241
|
+
stateDiagram-v2
|
|
242
|
+
[*] --> created
|
|
243
|
+
created --> attached: target validates binding
|
|
244
|
+
attached --> active: explicit start
|
|
245
|
+
active --> handoff_submitted: submit outcome and evidence
|
|
246
|
+
handoff_submitted --> accepted: coordinator accepts
|
|
247
|
+
handoff_submitted --> returned: coordinator returns with reason
|
|
248
|
+
active --> blocked: dependency or decision required
|
|
249
|
+
active --> abandoned: explicit discontinuation
|
|
250
|
+
handoff_submitted --> blocked: explicit follow-up required
|
|
251
|
+
```
|
|
252
|
+
|
|
253
|
+
| State | Meaning |
|
|
254
|
+
| --- | --- |
|
|
255
|
+
| `created` | A coordinator recorded an assignment. |
|
|
256
|
+
| `attached` | The target session validated its repository/worktree binding. |
|
|
257
|
+
| `active` | The bounded objective is in progress. |
|
|
258
|
+
| `handoff-submitted` | Immutable outcome evidence awaits coordinator disposition. |
|
|
259
|
+
| `accepted` / `returned` | The coordinator accepted the outcome or returned it with a reason. |
|
|
260
|
+
| `blocked` / `abandoned` | Evidence is preserved and explicit follow-up is required. |
|
|
261
|
+
|
|
262
|
+
## Prompt context and footer
|
|
263
|
+
|
|
264
|
+
Prompt injection is additive. Every session receives a concise named universal baseline that advertises explicit coordination tools and forbids inferred roles. Managed sessions additionally receive a named role section; neither section replaces the full system prompt or overrides higher-authority Pi and project instructions.
|
|
265
|
+
|
|
266
|
+
| Session | Prompt context | Footer |
|
|
267
|
+
| --- | --- | --- |
|
|
268
|
+
| Root coordinator | Direct active assignments and coordination obligations. | `🧭 Root coordinator · N focused` |
|
|
269
|
+
| Domain coordinator | Direct local assignments and delegated-boundary obligations. | `🧭 Domain coordinator · N focused` |
|
|
270
|
+
| Focused session | Parent identity, objective, scope, binding, escalation rules, and handoff format. | `🎯 Focused session` |
|
|
271
|
+
| Unmanaged | Universal coordination baseline only. | None. |
|
|
272
|
+
|
|
273
|
+
The root footer aggregates active focused sessions from direct assignments and domain descendants. It intentionally provides one count rather than a complex hierarchy breakdown.
|
|
274
|
+
|
|
275
|
+
For the exact injection contract, see [`docs/prompt-injection.md`](./docs/prompt-injection.md). The [documentation index](./docs/README.md) links stable coordination and operational references, contributor guidance, optional transport, the exploratory decision model, and local [release/version preparation](./docs/release-version-preparation.md).
|
|
276
|
+
|
|
277
|
+
## Safety boundaries
|
|
278
|
+
|
|
279
|
+
- One writer owns one writable checkout at a time.
|
|
280
|
+
- A role relationship narrows coordination context; it does not grant operational authority.
|
|
281
|
+
- Attachment rejects a mismatched repository/worktree binding.
|
|
282
|
+
- RDD ownership remains with the session that opened the lineage; it cannot transfer through a handoff.
|
|
283
|
+
- A message delivery acknowledgement is not acceptance, completion, or ownership transfer.
|
|
284
|
+
- Unmanaged sessions retain normal Pi behavior.
|
|
285
|
+
|
|
286
|
+
## Development
|
|
287
|
+
|
|
288
|
+
```bash
|
|
289
|
+
npm test
|
|
290
|
+
npm run build
|
|
291
|
+
```
|
|
292
|
+
|
|
293
|
+
The test suite covers canonical Git binding resolution, explicit attachment, lifecycle persistence, role boundaries, additive prompt composition, unmanaged fallback, aggregate footer behavior, documentation/version synchronization, and package-boundary verification.
|
|
294
|
+
|
|
295
|
+
### Command aliases and issue forms
|
|
296
|
+
|
|
297
|
+
The compatibility `/orchestrator <action>` command remains available. Its discoverable aliases are `/orchestrator:coordinator`, `:assign`, `:attach`, `:start`, `:handoff`, `:accept`, `:return`, `:decision`, `:block`, `:abandon`, and `:status`.
|
|
298
|
+
|
|
299
|
+
Use the repository's [bug report](https://github.com/davidjadczyk/pi-session-orchestrator/blob/main/.github/ISSUE_TEMPLATE/bug_report.yml) or [feature request](https://github.com/davidjadczyk/pi-session-orchestrator/blob/main/.github/ISSUE_TEMPLATE/feature_request.yml) form for public reports. Sanitize credentials, private paths, and session transcripts.
|
|
300
|
+
|
|
301
|
+
## Operational recovery and ledger
|
|
302
|
+
|
|
303
|
+
Coordinator footers count current children rather than only working sessions: `working` means active execution, `ready` means attached and able to begin, `pending` means an invitation awaits attachment, and `attention` means a handoff or blocker needs action. A pending target sees `⏳ Pending focused assignment` and an additive attachment notice; it is never auto-attached or promoted.
|
|
304
|
+
|
|
305
|
+
Session presence is separate from assignment role. Reloaded, suspended, and stale observations help a coordinator decide whether to retain, restore, release, or abandon work, but shutdown never detaches, deletes, or promotes a session. Presence becomes `stale` only when no observation has occurred for 30 minutes; this is derived at read time and starts no background timer. Restore a known session with `pi --session <id>`; use `/resume` or `pi -r` to browse the picker.
|
|
306
|
+
|
|
307
|
+
`orchestrator_ledger` is a bounded, read-only per-root view derived from typed assignments, lifecycle events, handoffs, and presence. Every returned collection is capped by the requested limit and includes `total`, `returned`, and `truncated` metadata. It keeps structured objectives, decisions, and handoff evidence, not Pi or `pi-intercom` transcripts. Future analysis may join a stored transport reference with the original message only when explicitly requested and authorized.
|
|
Binary file
|
package/docs/README.md
ADDED
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
# Documentation index
|
|
2
|
+
|
|
3
|
+
This index separates stable, current behavior from exploratory material and local release preparation.
|
|
4
|
+
|
|
5
|
+
## Stable documentation
|
|
6
|
+
|
|
7
|
+
- [Coordination model](reference/coordination-model.md) — explicit durable roles, assignments, and bounded hierarchy.
|
|
8
|
+
- [Operational boundaries](reference/operational-boundaries.md) — local state, authority limits, and what the extension never performs.
|
|
9
|
+
- [Prompt injection and context](prompt-injection.md) — additive context, precedence, and explicit delegation guidance.
|
|
10
|
+
- [Contributor guide](../CONTRIBUTING.md) — local development and validation.
|
|
11
|
+
- [Optional pi-intercom companion](pi-intercom.md) — transport relationship; `pi-intercom` is not a package dependency.
|
|
12
|
+
- [Release and version preparation](release-version-preparation.md) — local SemVer decisions and validation only.
|
|
13
|
+
|
|
14
|
+
## Exploratory documentation
|
|
15
|
+
|
|
16
|
+
- [Fast decision model](reference/fast-decision-model.md) — **Exploratory**; not a runtime contract or delivery promise.
|
|
17
|
+
|
|
18
|
+
See the [reference index](reference/README.md) for the stable/exploratory reference split.
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
# Optional pi-intercom companion
|
|
2
|
+
|
|
3
|
+
**Status: Stable integration boundary.**
|
|
4
|
+
|
|
5
|
+
`pi-intercom` may provide live message transport between independently running sessions. `pi-session-orchestrator` records explicit assignments, lifecycle events, handoffs, and optional transport references; it does not deliver messages or retain message transcripts.
|
|
6
|
+
|
|
7
|
+
`pi-intercom` is optional and is not a dependency of this package. Install and configure it independently when live transport is wanted. Its availability never creates a coordinator role, a managed assignment, operational authority, or delivery authority.
|
|
@@ -0,0 +1,52 @@
|
|
|
1
|
+
# Prompt injection contract
|
|
2
|
+
|
|
3
|
+
When `pi-session-orchestrator` is active, it injects a concise universal coordination baseline before each model run. When a session has explicit durable managed state, it also injects a concise role-specific section. Neither section is a second workflow policy or a source of authority.
|
|
4
|
+
|
|
5
|
+
## What users should configure
|
|
6
|
+
|
|
7
|
+
Users do **not** need to copy role or session context into `APPEND_SYSTEM.md`, `AGENTS.md`, or a custom system prompt when the extension is active. Keep durable, always-applicable policy in those normal locations. The extension supplies the changing root-coordinator, domain-coordinator, or focused-session facts from its managed assignment state.
|
|
8
|
+
|
|
9
|
+
## Prompt composition and precedence
|
|
10
|
+
|
|
11
|
+
A prompt has four complementary inputs:
|
|
12
|
+
|
|
13
|
+
1. **Always-on baseline policy** supplies stable safety, authority, and general operating rules.
|
|
14
|
+
2. **Extension-injected universal baseline** advertises explicit managed coordination and its narrow control-plane tools.
|
|
15
|
+
3. **Extension-injected role context** supplies current managed role and assignment facts only after an explicit durable transition.
|
|
16
|
+
4. **Repository/project instructions** supply repository-local conventions, commands, and delivery constraints.
|
|
17
|
+
|
|
18
|
+
The extension sections are factual and additive. They cannot override higher-authority system policy or repository/project instructions. An assignment may narrow the work a focused session performs, but it does not authorize actions that the governing policy or project instructions prohibit. If instructions conflict, follow the normal instruction hierarchy and the more restrictive applicable constraint.
|
|
19
|
+
|
|
20
|
+
Prompt composition is additive: this extension injects only its own named structured section and never replaces the whole system prompt. Later sections and sections owned by other extensions must remain intact.
|
|
21
|
+
|
|
22
|
+
## Universal coordination baseline
|
|
23
|
+
|
|
24
|
+
Every active extension session receives a named baseline that says small, known work may remain inline; managed multi-session coordination is available only through an explicit durable assignment; and roles are never inferred from conversation, repository, or session creation. `orchestrator_status` reads state and `orchestrator_update` records explicit state transitions. When work outgrows its bounded scope, record an assignment and use fast focused delegation rather than widening a session's context. Keep overarching coordination topics with coordinators and implementation detail with focused sessions; do not dump either across that boundary. Session creation and `pi-intercom` messaging remain separate concerns, and the extension transfers neither authority nor delivery authority.
|
|
25
|
+
|
|
26
|
+
This resolves the bootstrap problem: an unmanaged session knows how to enter explicit coordination without being falsely treated as a coordinator or focused session.
|
|
27
|
+
|
|
28
|
+
## Role-specific facts
|
|
29
|
+
|
|
30
|
+
For a root coordinator, the extension injects its canonical session identity, direct active assignments and lifecycle states, and its cross-boundary coordination and handoff obligations.
|
|
31
|
+
|
|
32
|
+
For a domain coordinator, it injects the same concise local coordination facts for its delegated boundary. It may coordinate focused sessions only.
|
|
33
|
+
|
|
34
|
+
For a focused session, it injects the parent/coordinator session ID, objective, allowed scope, canonical repository/worktree binding, escalation rules, and compact handoff format. It cannot create managed children. It retains implementation detail locally and escalates oversized work rather than absorbing broader coordination concerns.
|
|
35
|
+
|
|
36
|
+
The extension injects only the facts needed for that role and turn. A root footer may aggregate active focused descendants for display, but coordinator prompt context remains limited to direct assignments. The extension does not inject a full orchestration philosophy, unrelated assignment details, repository instructions, conversation history, secrets, or unsupported workflow state. Unknown or unavailable assignment data is omitted rather than invented.
|
|
37
|
+
|
|
38
|
+
## Pending assignments and operational recovery
|
|
39
|
+
|
|
40
|
+
A created assignment does not confer a managed role. When a target session ID has a pending assignment, the extension may inject a concise named attachment notice and show a pending footer so the target can validate its own binding and attach explicitly. Coordinators see operational child buckets: working, ready, pending, and attention. Session presence is recorded separately from assignment role; it becomes `stale` only after 30 minutes without an observation, and shutdown or stale presence never auto-detaches, promotes, or deletes a session. Recovery guidance is contextual: `pi --session <id>` restores a known session, while `/resume` or `pi -r` opens the picker.
|
|
41
|
+
|
|
42
|
+
`orchestrator_ledger` exposes a bounded read-only view derived from typed orchestration state. Its assignments, events, handoffs, and presence collections are each capped by the requested limit and report `total`, `returned`, and `truncated` metadata. It retains structured goals, decisions, handoffs, timestamps, and optional transport references, but never copies Pi or `pi-intercom` transcripts into the ledger or prompt.
|
|
43
|
+
|
|
44
|
+
## Unmanaged fallback
|
|
45
|
+
|
|
46
|
+
A session is unmanaged unless it explicitly attaches to a matching managed assignment. While the extension is active, an unmanaged session receives the universal baseline but no role/session section and no footer. If the extension is inactive or unavailable, it injects nothing. The extension makes no role inference from conversation text, working directory, or a session tree; the session otherwise continues with its normal Pi, project, and user-provided prompt configuration.
|
|
47
|
+
|
|
48
|
+
## Tool-driven bootstrap and adoption
|
|
49
|
+
|
|
50
|
+
`orchestrator_status` returns the current role, footer, pending attachment, direct assignments, and next prompt-context state. `orchestrator_update` records explicit `register-root`, `assign-existing`, `attach`, lifecycle, handoff, settlement, and terminal transitions.
|
|
51
|
+
|
|
52
|
+
A root may atomically adopt existing known sessions in an `assign-existing` batch. Both `orchestrator_update({ operation: "assign-existing" })` and `/orchestrator assign` require full canonical Pi session UUIDs; abbreviated display IDs are rejected. Assignment creation never creates a session or worktree. Each target remains unmanaged until it explicitly attaches from its own matching canonical Git worktree. A successful transition reports the new state immediately; role-specific injection begins on the next model run.
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
# Reference
|
|
2
|
+
|
|
3
|
+
Reference pages separate stable, current behavior from exploration.
|
|
4
|
+
|
|
5
|
+
## Stable
|
|
6
|
+
|
|
7
|
+
- [Coordination model](coordination-model.md) — **Stable current behavior**.
|
|
8
|
+
- [Operational boundaries](operational-boundaries.md) — **Stable current behavior**.
|
|
9
|
+
|
|
10
|
+
## Exploratory
|
|
11
|
+
|
|
12
|
+
- [Fast decision model](fast-decision-model.md) — **Exploratory**, not a runtime contract.
|
|
13
|
+
|
|
14
|
+
For prompt composition, contributor guidance, optional transport, and release preparation, use the [documentation index](../README.md).
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
# Coordination model
|
|
2
|
+
|
|
3
|
+
**Status: Current behavior.**
|
|
4
|
+
|
|
5
|
+
The extension records explicit coordinator and focused-session roles, assignments, canonical repository/worktree bindings, lifecycle transitions, handoffs, and typed decisions. Relationships are not inferred from conversation, session topology, repository location, or worktree location.
|
|
6
|
+
|
|
7
|
+
Managed depth is limited to root coordinator → domain coordinator → focused session. Unmanaged sessions retain normal Pi behavior. Agent-callable `orchestrator_*` tools are the normal automated control plane; human `/orchestrator` commands provide direct operation and recovery.
|
|
8
|
+
|
|
9
|
+
Each assignment carries a durable scope revision (legacy state hydrates revision `1`). A focused session can use `propose-scope-change` with its current revision, a narrow requested scope, a reason, and evidence. Only the explicitly assigned coordinator can approve that current proposal; approval updates the assignment and increments the revision. A focused session may also record `approve-scope-change-human`, which updates the scope and revision immediately and creates a pending coordinator reconciliation obligation. Reconciliation acknowledges the change and does not approve it a second time. Stale proposals or revisions fail closed before state is written. These controls do not grant filesystem, worktree, session-management, delivery, or Git authority.
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
# Fast decision model
|
|
2
|
+
|
|
3
|
+
**Status: Exploratory.** This brief is a Jev/fast-decision-model exploration, not a runtime commitment or delivery promise.
|
|
4
|
+
|
|
5
|
+
## Intent
|
|
6
|
+
|
|
7
|
+
Explore a compact way to frame routine decisions while preserving explicit ownership, evidence, and human authority for consequential choices.
|
|
8
|
+
|
|
9
|
+
## Candidate decision flow
|
|
10
|
+
|
|
11
|
+
1. State the decision and bounded scope.
|
|
12
|
+
2. Gather local evidence and list constraints.
|
|
13
|
+
3. Record options, recommendation, and reversible next step.
|
|
14
|
+
4. Escalate product, security, cost, destructive, or remote-state choices.
|
|
15
|
+
5. Record the outcome and evidence for later review.
|
|
16
|
+
|
|
17
|
+
## Open questions
|
|
18
|
+
|
|
19
|
+
- Which decision classes benefit from a shorter template?
|
|
20
|
+
- How should uncertainty and expiry be represented?
|
|
21
|
+
- What evidence threshold is useful without creating a new runtime contract?
|
|
22
|
+
|
|
23
|
+
This page is exploratory only. It is not a runtime commitment, API specification, authorization mechanism, or promise of delivery.
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
# Operational boundaries
|
|
2
|
+
|
|
3
|
+
**Status: Current behavior.**
|
|
4
|
+
|
|
5
|
+
The extension records coordination state and provides additive prompt context and display-only status. It does not create sessions, branches, worktrees, processes, releases, commits, pushes, merges, deployments, or publication actions. A handoff is evidence and never delivery authorization.
|
|
6
|
+
|
|
7
|
+
Human product, security, cost, destructive, credential, and remote-state decisions remain outside the extension. State is local and explicit; the compatibility `/orchestrator <action>` command and namespaced aliases use the same command implementation.
|
|
@@ -0,0 +1,55 @@
|
|
|
1
|
+
# Release and version preparation
|
|
2
|
+
|
|
3
|
+
Use this guide to prepare a local candidate. It does not authorize delivery or run npm publishing locally.
|
|
4
|
+
|
|
5
|
+
## Choose the version
|
|
6
|
+
|
|
7
|
+
Follow SemVer:
|
|
8
|
+
|
|
9
|
+
- **Major:** incompatible public API, documented behavior, or packaging-contract change.
|
|
10
|
+
- **Minor:** backward-compatible user-visible capability or additive documented contract.
|
|
11
|
+
- **Patch:** backward-compatible bug fix or documentation correction with no new capability.
|
|
12
|
+
- **Before 1.0:** treat incompatible public-contract changes as minor; use patch for compatible fixes. State the compatibility impact explicitly.
|
|
13
|
+
|
|
14
|
+
Version `0.2.0` is a minor release because it adds user-visible explicit delegation guidance and packaged documentation while preserving existing APIs and dependencies.
|
|
15
|
+
|
|
16
|
+
## Pi installation and discovery
|
|
17
|
+
|
|
18
|
+
- Keep `pi-package` in `package.json` keywords so the package gallery can discover the package.
|
|
19
|
+
- Keep `pi.extensions` as the resource manifest for the extension entry point.
|
|
20
|
+
- Keep the gallery image on the public `main` URL so the registry metadata remains stable:
|
|
21
|
+
`https://raw.githubusercontent.com/davidjadczyk/pi-session-orchestrator/main/assets/pi-session-orchestrator-thumbnail-pi-sessions.png`.
|
|
22
|
+
- After publication, users install the package with `pi install npm:pi-session-orchestrator`.
|
|
23
|
+
|
|
24
|
+
## Prepare locally
|
|
25
|
+
|
|
26
|
+
1. Update `package.json` and the root `package-lock.json` to the same valid SemVer version.
|
|
27
|
+
2. Keep Pi runtime packages, including `typebox`, in `peerDependencies` with `"*"`; retain development pins only for local type-checking and tests.
|
|
28
|
+
3. Update package-facing documentation and its distribution allowlist.
|
|
29
|
+
4. Run:
|
|
30
|
+
|
|
31
|
+
```sh
|
|
32
|
+
npm test
|
|
33
|
+
npm run build
|
|
34
|
+
npm run verify-pack
|
|
35
|
+
npm pack --dry-run --json --ignore-scripts
|
|
36
|
+
ruby -e 'require "yaml"; ARGV.each { |path| YAML.safe_load_file(path) }' .github/workflows/ci.yml .github/ISSUE_TEMPLATE/bug_report.yml .github/ISSUE_TEMPLATE/feature_request.yml
|
|
37
|
+
git diff --check
|
|
38
|
+
git diff --cached --name-only
|
|
39
|
+
```
|
|
40
|
+
|
|
41
|
+
4. Inspect the dry-run package list and verify every README-linked package document is included.
|
|
42
|
+
|
|
43
|
+
## Protected-branch release procedure
|
|
44
|
+
|
|
45
|
+
1. Configure the repository externally with protected `main` as the default branch. Require pull requests and the repository's normal required checks before merging.
|
|
46
|
+
2. Configure npm trusted publishing externally for `davidjadczyk/pi-session-orchestrator` using GitHub Actions, workflow file `.github/workflows/publish.yml`, and the public npm registry. The npm package settings, not this repository, store that trusted-publisher relationship.
|
|
47
|
+
3. Merge the reviewed release to protected `main`, create an exact annotated tag such as `v0.2.0`, and push the tag through the normal authorized repository process.
|
|
48
|
+
4. Manually dispatch **Publish package** from `main` with the exact tag. The workflow rejects non-`vSemVer` input, lightweight tags, tag/main divergence, version mismatches, and any run that is not based on remote `main`.
|
|
49
|
+
5. The workflow runs `npm ci`, tests, build, package verification, and `npm pack --dry-run` before `npm publish --provenance` with GitHub OIDC. No npm token is stored in the repository or used for this release path.
|
|
50
|
+
|
|
51
|
+
The external prerequisites are: protected `main` and its required checks, the annotated-tag push permission, and npm trusted-publisher configuration for this exact repository/workflow. Verify them in their respective systems before attempting a dispatch.
|
|
52
|
+
|
|
53
|
+
## Delivery boundary
|
|
54
|
+
|
|
55
|
+
Commits, pushes, tags, GitHub releases, remote Actions observation, and npm publishing each require separate authorization. Local preparation must never publish to npm, create a tag or release, dispatch the workflow, or change GitHub settings.
|
package/package.json
ADDED
|
@@ -0,0 +1,58 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "pi-session-orchestrator",
|
|
3
|
+
"version": "0.2.0",
|
|
4
|
+
"type": "module",
|
|
5
|
+
"description": "Explicit, durable coordinator and focused-session state for Pi",
|
|
6
|
+
"license": "MIT",
|
|
7
|
+
"repository": {
|
|
8
|
+
"type": "git",
|
|
9
|
+
"url": "git+https://github.com/davidjadczyk/pi-session-orchestrator.git"
|
|
10
|
+
},
|
|
11
|
+
"bugs": {
|
|
12
|
+
"url": "https://github.com/davidjadczyk/pi-session-orchestrator/issues"
|
|
13
|
+
},
|
|
14
|
+
"homepage": "https://github.com/davidjadczyk/pi-session-orchestrator",
|
|
15
|
+
"publishConfig": {
|
|
16
|
+
"access": "public",
|
|
17
|
+
"registry": "https://registry.npmjs.org/"
|
|
18
|
+
},
|
|
19
|
+
"keywords": [
|
|
20
|
+
"pi",
|
|
21
|
+
"pi-extension",
|
|
22
|
+
"pi-package",
|
|
23
|
+
"orchestration",
|
|
24
|
+
"session-coordination"
|
|
25
|
+
],
|
|
26
|
+
"engines": {
|
|
27
|
+
"node": ">=22.19.0"
|
|
28
|
+
},
|
|
29
|
+
"files": [
|
|
30
|
+
"src/",
|
|
31
|
+
"README.md",
|
|
32
|
+
"LICENSE",
|
|
33
|
+
"assets/pi-session-orchestrator-thumbnail-pi-sessions.png",
|
|
34
|
+
"docs/"
|
|
35
|
+
],
|
|
36
|
+
"pi": {
|
|
37
|
+
"extensions": [
|
|
38
|
+
"./src/index.ts"
|
|
39
|
+
],
|
|
40
|
+
"image": "https://raw.githubusercontent.com/davidjadczyk/pi-session-orchestrator/main/assets/pi-session-orchestrator-thumbnail-pi-sessions.png"
|
|
41
|
+
},
|
|
42
|
+
"scripts": {
|
|
43
|
+
"build": "tsc --noEmit",
|
|
44
|
+
"test": "node --test --import tsx test/*.test.ts",
|
|
45
|
+
"verify-pack": "node scripts/verify-pack.mjs"
|
|
46
|
+
},
|
|
47
|
+
"peerDependencies": {
|
|
48
|
+
"@earendil-works/pi-coding-agent": "*",
|
|
49
|
+
"typebox": "*"
|
|
50
|
+
},
|
|
51
|
+
"devDependencies": {
|
|
52
|
+
"@earendil-works/pi-coding-agent": "0.85.1",
|
|
53
|
+
"@types/node": "22.10.10",
|
|
54
|
+
"tsx": "4.19.2",
|
|
55
|
+
"typebox": "1.3.7",
|
|
56
|
+
"typescript": "5.7.3"
|
|
57
|
+
}
|
|
58
|
+
}
|
package/src/binding.ts
ADDED
|
@@ -0,0 +1,50 @@
|
|
|
1
|
+
import { lstatSync, readFileSync, realpathSync } from "node:fs";
|
|
2
|
+
import { dirname, isAbsolute, join, resolve } from "node:path";
|
|
3
|
+
|
|
4
|
+
import type { CanonicalBinding } from "./model";
|
|
5
|
+
|
|
6
|
+
function gitDirectory(worktree: string): string {
|
|
7
|
+
const dotGit = join(worktree, ".git");
|
|
8
|
+
const metadata = lstatSync(dotGit);
|
|
9
|
+
if (metadata.isDirectory()) return dotGit;
|
|
10
|
+
if (!metadata.isFile()) throw new Error("Git metadata is not a file or directory");
|
|
11
|
+
|
|
12
|
+
const match = /^gitdir:\s*(.+?)\s*$/m.exec(readFileSync(dotGit, "utf8"));
|
|
13
|
+
if (!match) throw new Error("Git metadata file has no gitdir");
|
|
14
|
+
return isAbsolute(match[1]) ? match[1] : resolve(worktree, match[1]);
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
function worktreeFor(cwd: string): string {
|
|
18
|
+
let directory = realpathSync(cwd);
|
|
19
|
+
while (true) {
|
|
20
|
+
try {
|
|
21
|
+
gitDirectory(directory);
|
|
22
|
+
return directory;
|
|
23
|
+
} catch {
|
|
24
|
+
const parent = dirname(directory);
|
|
25
|
+
if (parent === directory) throw new Error("No Git worktree found");
|
|
26
|
+
directory = parent;
|
|
27
|
+
}
|
|
28
|
+
}
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
function commonGitDirectory(gitDir: string): string {
|
|
32
|
+
try {
|
|
33
|
+
return resolve(gitDir, readFileSync(join(gitDir, "commondir"), "utf8").trim());
|
|
34
|
+
} catch {
|
|
35
|
+
return gitDir;
|
|
36
|
+
}
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
/** Resolves canonical Git metadata from filesystem worktree metadata without spawning Git. */
|
|
40
|
+
export function activeCanonicalBinding(cwd: string): CanonicalBinding {
|
|
41
|
+
try {
|
|
42
|
+
const worktree = worktreeFor(cwd);
|
|
43
|
+
return {
|
|
44
|
+
repository: realpathSync(commonGitDirectory(gitDirectory(worktree))),
|
|
45
|
+
worktree,
|
|
46
|
+
};
|
|
47
|
+
} catch {
|
|
48
|
+
throw new Error("A managed assignment requires an active Git repository/worktree binding");
|
|
49
|
+
}
|
|
50
|
+
}
|