Skip to content

Commit da458ed

Browse files
committed
feat: wake sessions for available answers
1 parent c4fcfa9 commit da458ed

17 files changed

Lines changed: 637 additions & 37 deletions

README.md

Lines changed: 7 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,7 @@ The product requirements document is in [`docs/prd/pi-postbox.md`](docs/prd/pi-p
88

99
## Current status
1010

11-
Version 0.2.7 coordinates protocol 0.1.9 with Android 0.4.2 (build 6), generated cross-language conformance fixtures, exact-match version gating, and hard-block mismatch UX.
11+
Version 0.2.9 coordinates protocol 0.1.9 with Android 0.4.2 (build 6), generated cross-language conformance fixtures, exact-match version gating, hard-block mismatch UX, durable Answer-driven agent auto-wake, and answer-ready widget cleanup after reads.
1212

1313
Issues #1-#11 provide the v1 implementation: runnable TypeScript workspace, `pi-postbox-server` CLI, Pi extension with `write_question`, WebSocket session registration, SSE browser state, SQLite persistence/history, structured Questions and options, semantic working/blocked/idle state, reconnect/idempotency/expiry, local terminal fallback commands, editable presentation metadata, and packaging/deployment docs plus a release smoke script. Version 0.2.6 updates Android 0.4.1 to include the server-required Question revision when submitting an Answer. Version 0.2.5 keeps the footer and status surfaces from undercounting a locally tracked open Question when the durable owner snapshot is briefly stale, makes published package updates robust on npm 11, and prebuilds the shared protocol before clean-checkout test runs. Version 0.2.3 adds validated npm Trusted Publishing from pushes to the main branch through GitHub Actions OIDC. Version 0.2.2 adds complete npm license and source metadata, fixes the published CLI bin path, and excludes test source from the package tarball. Version 0.2.1 bounds and paginates every model-facing bulk read, uses compact stateless cursors, hides inactive historical owners by default, trims repeated list fields, and preserves checkout development ports across restarts. Version 0.2.0 replaces the separate model-facing create/update tools with one explicit-action `write_question` surface and returns reusable current Question handles from creation. Version 0.1.9 requires a concise ambiguity for new Questions, simplifies parent and expiry inputs, renames option `meaning` to `impact`, removes top-level handoff context and per-option context, removes reconstructed Question Chat, and renders single/multi choice with accessible ballot controls. Version 0.1.8 exposed single-Question create/idempotent receipt disposition and safely required a full Pi restart when `/reload` retained an incompatible shared protocol dependency. Version 0.1.7 reduced model-facing tool schemas, strictly described exact-owner filters, and made recovery reads compact by default with an explicit full view while preserving strict server-side action validation and internal provenance/expiry compatibility.
1414

@@ -82,7 +82,7 @@ pi-postbox-server
8282

8383
## Agent tool contracts
8484

85-
`write_question` is the single model-facing write surface. Use `action: "create"` or `action: "create_batch"` to persist Questions, and `revise`, `cancel`, `supersede`, `reparent`, `transfer`, or `takeover` to change an existing Question. Creation returns after durable persistence rather than waiting for a human Answer. Continue any independent work and do not poll `get_answer`, `list_question_status`, or `list_questions`: Postbox notifies the owning Pi Session when an Answer is available. If that human decision becomes the only remaining blocker, call `wait_for_postbox` once to enter explicit idle/waiting mode; after it wakes, read the relevant Question with `get_answer`.
85+
`write_question` is the single model-facing write surface. Use `action: "create"` or `action: "create_batch"` to persist Questions, and `revise`, `cancel`, `supersede`, `reparent`, `transfer`, or `takeover` to change an existing Question. Creation returns after durable persistence rather than waiting for a human Answer. Continue any independent work and do not poll `get_answer`, `list_question_status`, or `list_questions`: by default, Postbox coalesces Answer notifications and starts a follow-up agent turn for an owning Pi Session that is no longer running. The privacy-preserving wake contains Question identifiers, never Answer content, and tells the agent to read with `get_answer`. If that human decision becomes the only remaining blocker during the current turn, `wait_for_postbox` remains available as an explicit idle/waiting mode.
8686

8787
Batch idempotency is per Question: put a stable `requestId` on each item. A top-level batch `requestId` is invalid because Postbox does not claim an atomic batch-level idempotency contract. Questions may refer to an earlier item with `parentLocalRef`; the server validates the ordered batch before persisting independent Question records:
8888

@@ -112,7 +112,7 @@ Agent query tools use small workflow-oriented results by default:
112112
- `list_questions` returns at most 100 records per page; `list_question_status` returns at most 50. Both accept only `scope` and a strict `{ "harness": "…", "ownerId": "…" }` owner filter on the model-facing surface, and return `nextCursor` when another page exists.
113113
- `list_postbox_owners()` derives the caller's feature scope, omits offline zero-count historical owners by default, and returns paged coarse presence and active/unread counts (100 maximum per page). Set `includeInactive: true` only for audit workflows.
114114
- `get_postbox_owner_status({ owners })` accepts at most 20 exact owner identities.
115-
- `get_answer({ questionId })` returns `{ "type": "pending", "status": "pending", "questionId": "…" }` while the Question remains unresolved; this is a normal bounded result, not an error.
115+
- `get_answer({ questionId })` returns `{ "type": "pending", "status": "pending", "questionId": "…" }` while the Question remains unresolved; this is a normal bounded result, not an error. After it successfully reads an Answer, the extension clears only that Answer's matching answer-ready widget.
116116

117117
Pagination cursors are opaque, compact, stateless, and bound to the original query. Copy them unchanged into the next call with the same filters; malformed or cross-query cursors are rejected.
118118

@@ -149,10 +149,13 @@ The extension resolves a server profile from the loaded package. Installed npm/g
149149

150150
```json
151151
{
152-
"serverUrl": "http://127.0.0.1:32187"
152+
"serverUrl": "http://127.0.0.1:32187",
153+
"autoWake": true
153154
}
154155
```
155156

157+
Answer auto-wake is enabled by default. Set `"autoWake": false` in the profile config, or set `PI_POSTBOX_AUTO_WAKE=off`, to keep widget-only notifications without starting an agent turn. `PI_POSTBOX_AUTO_WAKE=on` overrides a disabled config value.
158+
156159
Override config location with `PI_POSTBOX_CONFIG_PATH` or `PI_POSTBOX_CONFIG_DIR`. The extension creates a generated machine id on first startup and persists it in this config file. That generated machine id is stable across sessions; hostname and dashboard aliases provide human-readable names.
157160

158161
For local self-healing, each profile publishes only `<profile-state-dir>/active-local/server.json`. The extension validates that record against `/healthz` profile, instance, URL, protocol, and build identity. Health reports the package version separately from the protocol version, while the default build id fingerprints the loaded runtime bytes. It never orders or falls back across profiles. A global production loopback `serverUrl` is therefore invisible to a checkout development profile, while `PI_POSTBOX_URL` remains an intentional escape hatch.

apps/web/package.json

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
{
22
"name": "@pi-postbox/web",
3-
"version": "0.2.7",
3+
"version": "0.2.9",
44
"private": true,
55
"type": "module",
66
"scripts": {
@@ -10,7 +10,7 @@
1010
"typecheck": "svelte-check --tsconfig ./tsconfig.json"
1111
},
1212
"dependencies": {
13-
"@pi-postbox/protocol": "0.2.7",
13+
"@pi-postbox/protocol": "0.2.9",
1414
"marked": "^18.0.5"
1515
},
1616
"devDependencies": {

docs/configuration.md

Lines changed: 9 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -74,10 +74,15 @@ Example config:
7474

7575
```json
7676
{
77-
"serverUrl": "http://127.0.0.1:32187"
77+
"serverUrl": "http://127.0.0.1:32187",
78+
"autoWake": true
7879
}
7980
```
8081

82+
Answer auto-wake is enabled by default. When an Answer arrives while the owning Pi Session is not explicitly waiting, the extension durably records a wake intent, coalesces notifications arriving in the same short burst, and injects a privacy-preserving follow-up message that starts a turn if the agent is idle. The message includes Question identifiers but no Question text or Answer content. If Pi stops after recording the intent but before persisting the follow-up message, the active session branch recovers the wake after restart without duplicating already-persisted wakes.
83+
84+
Set `"autoWake": false` to retain the answer-ready widget without starting an agent turn. The `PI_POSTBOX_AUTO_WAKE` environment variable overrides the JSON value; accepted enabling values are `1`, `true`, `yes`, `on`, and `enabled`, while `0`, `false`, `no`, `off`, and `disabled` disable it.
85+
8186
The extension creates and persists a generated machine id in this same config file on first use. That generated machine id is the stable identity used by the dashboard. Hostname is also sent for display, and the dashboard can persist a friendlier machine alias.
8287

8388
Server payload limits are finite even though rich interviewer context is allowed: HTTP bodies and extension WebSocket messages are capped, rich text fields/options have generous schema limits, and project icons are limited to small image data URLs uploaded by the extension.
@@ -114,7 +119,9 @@ The icon path is resolved by the extension on the Pi machine, converted into a s
114119

115120
## Agent notification and explicit waiting
116121

117-
The `write_question` create actions return after durable persistence and include a reusable current Question handle. The owning Pi Session receives a lightweight notification when an Answer becomes available, so agents should continue independent work and must not poll `get_answer`, `list_question_status`, or `list_questions`. When the decision is the sole remaining blocker, call `wait_for_postbox` once to enter explicit idle/blocked mode; after it wakes, call `get_answer` for the relevant Question. Cancelling that ephemeral wait leaves durable Questions and Answers intact.
122+
The `write_question` create actions return after durable persistence and include a reusable current Question handle. Agents should continue independent work and must not poll `get_answer`, `list_question_status`, or `list_questions`. With default auto-wake enabled, an Answer notification starts a privacy-preserving follow-up turn when the owning Pi Session is idle; the agent then calls `get_answer` for the notified Question identifiers. A successful Answer read clears only the matching answer-ready widget; pending and lifecycle-only reads leave Answer widgets unchanged. Multiple notifications in the batching window produce one turn.
123+
124+
When the decision is the sole remaining blocker during an active turn, `wait_for_postbox` can still enter explicit idle/blocked mode. The server does not send proactive Answer notifications while that owner is explicitly waiting, so the wait result resumes the existing turn without also scheduling an auto-wake follow-up. Cancelling that ephemeral wait leaves durable Questions and Answers intact. Disable auto-wake with `autoWake: false` or `PI_POSTBOX_AUTO_WAKE=off` when widget-only notification behavior is preferred.
118125

119126
## Local fallback commands and browser command
120127

docs/protocol.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -283,7 +283,7 @@ Package-local autostart is a client recovery behavior for `write_question` and t
283283
- Treat `requestId`, `sessionId`, `machineId`, and `projectId` as stable protocol identifiers.
284284
- Handle unknown fields gracefully.
285285
- Use `/healthz` to confirm service and protocol version before relying on newer fields.
286-
- Package version 0.2.7 uses protocol 0.1.9 and Android 0.4.2 (versionCode 6). Android exact-matches its generated supported version, versions every consumed envelope, and hard-blocks incompatible connection-scoped workflows before decoding or side effects.
286+
- Package version 0.2.9 uses protocol 0.1.9 and Android 0.4.2 (versionCode 6). Android exact-matches its generated supported version, versions every consumed envelope, and hard-blocks incompatible connection-scoped workflows before decoding or side effects.
287287
- Question Chat is exact-fork only. `forkKind` accepts only `"exact"`; context-only activation endpoints and relay commands are not part of the current protocol.
288288
- Package version 0.2.6 retains protocol compatibility version 0.1.8 and updates Android 0.4.1 to submit the current Question revision required by the Answer endpoint. Package version 0.2.5 retains protocol compatibility version 0.1.8, prevents stale durable owner counts from hiding locally tracked open Questions in status surfaces, makes published package updates robust on npm 11, and prebuilds the shared protocol before clean-checkout test runs. Package version 0.2.3 retains protocol compatibility version 0.1.8 and adds validated npm Trusted Publishing from pushes to the main branch through GitHub Actions OIDC. Package version 0.2.2 retained protocol compatibility version 0.1.8 and changed release packaging metadata only. Package version 0.2.1 introduced protocol compatibility version 0.1.8, adds server-enforced bulk-read limits and paged status/history/owner envelopes, emits compact stateless cursors, and omits inactive owners by default. Matching extension/server/protocol builds are required. Package version 0.2.0 used protocol 0.1.7, merged Question creation and updates into the model-facing `write_question` tool, and extended create receipts with current content/ownership revisions and lifecycle status. Package version 0.1.9 used protocol 0.1.6 and required ambiguity on new Questions, simplified model-facing parent/expiry fields, renamed legacy option `meaning` to `impact`, removed top-level handoff context and per-option context, removed reconstructed Question Chat, and used exact-fork Chat only. Package version 0.1.8 added single-Question receipt disposition and suppressed autostart when exact live-profile identity proved that `/reload` retained an incompatible protocol dependency; a full Pi restart was then required. Package version 0.1.7 added compact recovery views, explicit exact-owner schemas, and a compact model-facing update schema.
289289
- V1 has no app-level authentication; restrict network reachability with Tailscale/lizardtail or an external auth proxy.

package-lock.json

Lines changed: 9 additions & 9 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

package.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
{
22
"name": "@wienerberliner/pi-postbox",
3-
"version": "0.2.7",
3+
"version": "0.2.9",
44
"description": "Pi Postbox extension and web service for remote human decision handoffs.",
55
"license": "MIT",
66
"type": "module",

packages/extension/package.json

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
{
22
"name": "@pi-postbox/extension",
3-
"version": "0.2.7",
3+
"version": "0.2.9",
44
"description": "Pi extension that registers sessions with Pi Postbox and exposes write_question.",
55
"type": "module",
66
"main": "src/index.ts",
@@ -32,7 +32,7 @@
3232
},
3333
"dependencies": {
3434
"@earendil-works/pi-coding-agent": "0.80.10",
35-
"@pi-postbox/protocol": "0.2.7",
35+
"@pi-postbox/protocol": "0.2.9",
3636
"typebox": "^1.1.38",
3737
"ws": "^8.21.0"
3838
},

0 commit comments

Comments
 (0)