Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
18 commits
Select commit Hold shift + click to select a range
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
143 changes: 143 additions & 0 deletions WS5-REMAINDER.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,143 @@
# WS5 remainder — OpenPhone conformance to Interfaces Protocol v1

Status of WS5 (Interfaces v1 rewrite plan, Part III) on `rewrite/protocol-v1`:
the working vertical slice is done — vendored ifp/1 validator/machine/fixtures,
the `openphone-tools.ifp1.json` manifest re-expressing all 37 commands, the
legacy-event -> action-lifecycle adapter with lifecycle tests, and deprecation
notices on the v0 manifests — all wired into `scripts/check.sh`
(see `runtime/protocol/ifp1/` and `runtime/protocol/ifp1/MIGRATION.md`).
This file lists what remains before the WS5 exit gate.

## 1. Java-side wiring: RuntimeToolBridge / PhoneToolGateway -> ifp/1

- `overlay/packages/apps/OpenPhoneAssistant/src/org/openphone/assistant/runtime/RuntimeToolBridge.java`
is where the v0 lifecycle lives today: it validates commands against the v0
manifest, applies autonomy policy, creates `runtime-confirm-*` confirmations
for mutating tools, executes, and returns tool results. Port it to emit and
consume ifp/1 action messages using the mapping in
`runtime/protocol/ifp1/adapter.mjs` (proposal ingestion -> local
confirmation -> `action.approve`/`action.reject` with canonical
`params_hash` binding -> idempotent execution -> `action.result` with
`binding_mismatch`/`action_expired` failure classes). A Java
canonical-JSON + SHA-256 helper must reproduce
`runtime/protocol/ifp1/fixtures/params-hash-vectors.json` exactly.
- `overlay/packages/apps/OpenPhoneAssistant/src/org/openphone/assistant/platform/PhoneToolGateway.java`
(and `OpenPhoneOsToolGateway.java`) — the seam from `refactor/app-os-boundary`
is already aligned with the target; the ifp/1 executor should call through
it unchanged. Do not redo the seam.
- Tool-name translation at the boundary: Java command registries key on
`openphone.<tool>` while ifp/1 uses `<tool>`
(`x-openphone.superseded_command` in `openphone-tools.ifp1.json` is the
lookup bridge).

## 2. Ed25519 pairing alignment

The OpenClaw adapter already has Ed25519 device identity:

- `overlay/packages/apps/OpenPhoneAssistant/src/org/openphone/assistant/runtime/adapters/openclaw/OpenClawEd25519.java`
- `overlay/packages/apps/OpenPhoneAssistant/src/org/openphone/assistant/runtime/adapters/openclaw/OpenClawDeviceIdentity.java`

Remaining work: promote this from an OpenClaw-adapter detail to the surface's
ifp/1 identity (spec §6.1–6.3): keypair generated at pairing with the entity's
home, `pair.request`/`pair.grant`/`pair.complete` challenge signature, and a
`control.hello` `payload.proof` signature over the transport nonce
(`x-ifp-nonce` for WebSocket). Key storage should move to the OS keystore
path. No default/dev credential may exist in any configuration.

## 3. OpenClaw / MCP / CLI adapter migration

All three still speak v0 and read `runtime/protocol/openphone-commands.json`:

- `integrations/openclaw-plugin/src/index.ts` (+ `dist/`) — command buckets
(safe read / private read / dangerous) become ifp/1 tool capabilities and
surface-local policy.
- `integrations/mcp-server/src/index.mjs` — MCP tools should be generated
from `openphone-tools.ifp1.json`; the `serverInfo.runtimeProtocol` range
advertisement becomes ifp/1 `control.hello` protocol negotiation semantics.
- `integrations/cli/src/index.mjs` — same manifest switch; `openphone info`
advertises ifp/1.
- The byte-sync package copies under `integrations/cli/runtime/protocol/` and
`integrations/mcp-server/runtime/protocol/` (enforced by
`tests/integrations/runtime-package-contract.mjs`) must then vendor the
ifp1 manifest instead.
- `runtime/protocol/openphone-runtime-tools.mjs` grows an ifp/1 loader (or a
successor module under `ifp1/`) so `mcpTools()`/`commandMap()` consumers
migrate without ad-hoc parsing.

## 4. Surface/entity ownership stubs (plan WS5 deliverable 3)

Re-label the system_server memory/context/audit stores (patches 0014–0016)
as surface-owned offline cache + audit evidence; stub the sync boundary so
durable-memory writes emit ifp/1 `event.journal.append` to a runtime endpoint
(pointed at the WS3 runtime in a local harness). OS-owned audit stays
phone-permanent; no framework patch is reverted.

## 5. Old-manifest deletion gate

`runtime/protocol/openphone-commands.json` and `openphone-events.json` are
deprecated (`"deprecated": true`, `"superseded_by": "ifp1/"`) but MUST NOT be
deleted until all of the following hold:

1. Items 1–3 above are done (no Java, plugin, MCP, or CLI reader of the v0
manifests remains).
2. The v0 sections of `runtime/protocol/validate-runtime-protocol.mjs`,
`openphone-runtime-tools.mjs`, and the v0 contract tests are replaced by
ifp/1 equivalents (including the Android command-registry cross-checks,
re-pointed at `openphone-tools.ifp1.json`).
3. `docs/runtime/runtime-agent-protocol.md` is rewritten for ifp/1 (it names
the v0 manifests as single sources of truth).
4. The ifp/1 fixture corpus has frozen at `v1.0.0-draft` upstream (ADR 0004:
freeze happens after WS4/presence-kit e2e conformance), so the vendored
copies here stop moving.

Deletion is the clean major-version break (D5): no compat path ships.

## 6. iOS runtime note

`ios/` does not exist on this branch (the iOS runtime is in flight on
`add-ios-support`). When that line lands or rebases onto ifp/1 work, add to
its README: "This runtime will consume Interfaces Protocol v1; see
`runtime/protocol/ifp1/`." Per the plan, `add-ios-support` is out of scope
for WS5 and consumes ifp/1 later.

## 7. Device-in-loop conformance lane (exit gate)

The WS5 exit gate also requires: existing CI green (full Android build needs
a LineageOS tree — not runnable here), the fixture corpus passing against the
phone-side implementation (device-in-loop or emulator lane), and the
end-to-end local demo with the WS3 runtime (propose -> confirm -> approve
with exact binding -> execute -> audit on both sides; mismatched
approved-vs-executed params rejected by the phone). The node-side halves of
these checks exist today in `tests/integrations/ifp1-adapter-contract.mjs`.

## Spec divergences found (feed back to WS1 before corpus freeze)

1. **Spec §8.1 example `params_hash` is wrong.** The example proposal in
`interfaces-protocol-1.md` shows
`sha256:2c26b46b...` for params `{"to": "+15551234567", "body": "on my way"}`,
but that digest is SHA-256 of the literal string `"foo"`. The canonical
encoding `{"body":"on my way","to":"+15551234567"}` hashes to
`sha256:1e920af2c02daa49a449d1ebe938e08f196a9dfa6a3c05b36adbabbd7387193b`
(vector `spec_8_1_example_params` in
`runtime/protocol/ifp1/fixtures/params-hash-vectors.json`).
2. **No canonical-JSON test vectors in the shared fixture corpus.** The
validators only regex-check `sha256:<hex64>`; nothing cross-checks the
canonicalization itself (unicode handling, nested key sorting). Upstream
`fixtures/params-hash-vectors.json` into
`packages/interfaces-protocol/fixtures/` and both reference validators.
3. **Canonicalization needs one more sentence of precision.** "UTF-8, sorted
keys, no insignificant whitespace" should state: keys sorted at every
nesting depth by Unicode code point, strings serialized without ASCII
escaping of non-ASCII characters (UTF-8 bytes hashed), and numbers
restricted to interoperable forms (e.g. no `1.0` vs `1` ambiguity — or
forbid non-integer numbers in params).
4. **`action.propose` sender lacks generation fencing.** `generation` is
required on `event.*`, `action.approve`, and `action.result`, but a stale
runtime can still propose actions. Probably fine (the approve/result fence
catches it), but worth an explicit spec note.
5. **Risk taxonomy loses information.** v0 distinguishes `risk` from
`confirmation` (`none`/`ask_before_action`/`always`) and `default_exposure`.
ifp/1 folds these into `risk` + surface-local policy — intentional per
§8.2, but the tools manifest needed an `x-` extension
(`x-openphone`) to carry the policy inputs. If other surfaces need the
same, consider a standard optional field on tool capability declarations.
58 changes: 46 additions & 12 deletions docs/AGENT_RUNTIME_V1.md
Original file line number Diff line number Diff line change
Expand Up @@ -10,8 +10,8 @@ session.
- Persist background agent work as first-class jobs.
- Wake jobs from alarms, boot, package replacement, and explicit tool calls.
- Keep delivery, retries, diagnostics, and failure state with the job.
- Preserve the reviewed autonomy posture: background jobs do not silently
perform state-changing actions.
- Preserve the reviewed autonomy posture: background jobs pause before
state-changing actions and resume only from an exact Android-owned review.
- Keep model routing in the registry and schemas instead of Java keyword
routing.

Expand Down Expand Up @@ -45,18 +45,35 @@ An `agent_job` records durable background work.
"mode": "notification",
"notification_text": "Release notes changed"
},
"status": "active",
"status": "queued",
"created_at": 1781480000000,
"updated_at": 1781480000000,
"next_run_at": 1781483055000,
"running_at": 0,
"last_run_at": 0,
"last_result": "",
"failure_count": 0,
"failure_alert_at": 0
"failure_alert_at": 0,
"phase": "queued",
"progress_text": "Queued",
"progress_current": 0,
"progress_total": 0,
"checkpoint_json": {},
"pending_confirmation_id": "",
"pending_tool_request_json": {},
"last_surface_id": "",
"resume_token": "",
"last_event_at": 1781480000000,
"unread_result": false,
"paused_at": 0
}
```

Lifecycle states are `queued`, `running`, `waiting`, `awaiting_review`,
`paused`, `completed`, `failed`, and `stopped`. Readers continue accepting the
legacy `active` value during migration. Runtime dispatch may also persist
`dispatched` while an external runtime owns the next step.

Job types:

- `agent_turn`: run a bounded model/tool loop against a prompt.
Expand All @@ -77,7 +94,8 @@ model/tool trace and must not be dumped directly into the notification surface.

The assistant schedules a single alarm for the next active job. On wake it:

1. repairs stale `running` jobs,
1. expires pending reviews and repairs stale `running` or review-resolution
jobs,
2. marks due jobs `running`,
3. starts bounded background runners,
4. persists completion/failure,
Expand All @@ -89,13 +107,29 @@ service startup.

## Safety

Background jobs may call observe/read tools and terminal tools. State-changing
tools return `background.confirmation_required` instead of executing. The
foreground reviewed flow remains the only path for actions that mutate device,
account, communication, or external state.

This is intentionally conservative. The follow-up design can add reviewed
background approvals with a notification action and a resumable job checkpoint.
Background jobs may call observe/read tools and terminal tools directly. A
state-changing tool is never executed by the background model loop. Instead,
OpenPhone:

1. bounds and sanitizes the exact proposed tool arguments;
2. persists an `awaiting_review` checkpoint and 15-minute confirmation;
3. shows the exact action in an Android notification and AI Home run detail;
4. atomically claims the first Approve or Deny tap;
5. verifies the tool, parameter digest, runtime, phone session, job, expiry,
idempotency key, checkpoint, and resume token;
6. executes only the persisted request after approval, or records a structured
denial/timeout result;
7. resumes the job with that bound result instead of regenerating the action.

Provider secrets, raw authorization, screenshots, data URLs, large encoded
blobs, and unbounded context are rejected from checkpoints. Audit events store
digests and lifecycle metadata, not raw parameters. If the process dies while
an approved action is resolving, stale-run repair marks the outcome unknown
and does not replay it. A denied action can safely resume after restart.

The wire shapes are defined by `schemas/agent-job.schema.json` and
`schemas/background-confirmation.schema.json`; recorded valid, tampered, and
secret-bearing fixtures run in `./scripts/check.sh`.

## OpenClaw/Hermes Mapping

Expand Down
90 changes: 90 additions & 0 deletions docs/APP_OS_BOUNDARY.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,90 @@
# App and OS Boundary

OpenPhone has one product experience with two possible phone capability
profiles:

- **OpenPhone App** uses Android public APIs and user-granted roles and
permissions. It is the future Play-distributable profile.
- **OpenPhone OS** adds a privileged execution adapter, framework services,
SystemUI surfaces, keyguard configuration, and system policy enforcement.

Most product logic should be shared. The OS layer should remain a small,
auditable authority rather than becoming a second assistant implementation.

## Dependency Direction

```text
AI Home / generated surfaces / model and runtime adapters
|
v
PhoneToolGateway
/ \
v v
public Android app OpenPhone OS adapter
implementation (framework Binder client)
|
v
system_server / SystemUI
```

Code above `PhoneToolGateway` must not import `android.openphone` hidden APIs,
construct `FrameworkToolExecutor`, or decide that a privileged action is
authorized. It may request a registered tool, show review UI, and interpret a
structured result.

The gateway reports a stable profile and whether each tool is supported. A
smaller implementation must deny an unsupported tool explicitly; it must not
silently fall back to accessibility automation or a lower-risk action.

## Source Ownership

| Layer | Current source | Responsibility |
| --- | --- | --- |
| Portable product | `assistant/actions`, `model`, `orchestrator`, `runtime`, `surface`, and Compose UI | Conversation, model adapters, tool requests, adaptive UI, runtime sessions, and user review presentation |
| Phone boundary | `assistant/platform/PhoneToolGateway.java` | Stable app-to-phone execution contract and capability-profile seam |
| OpenPhone OS adapter | `assistant/platform/OpenPhoneOsToolGateway.java` and `assistant/agent/FrameworkToolExecutor.java` | Translate registered tools into the hidden OpenPhone framework manager and Android integrations |
| OS authority | `patches/frameworks_base`, Settings patches, SystemUI patches, and SELinux policy | Screen/input authority, secure confirmation, durable audit, island rendering, keyguard-aware behavior, and privileged state |

The current privileged APK is still the only build artifact. This boundary is
the first extraction step, not a claim that the APK can already be uploaded to
Google Play.

## Capability Placement

The public app profile can own AI Home as a user-selected launcher, immersive
window UI, voice and text interaction, generated surfaces, model/runtime
adapters, local history, public Android intents, and scheduled work that fits
normal Android limits.

The OS profile remains authoritative for:

- SystemUI-owned island and keyguard-safe rendering;
- non-secure lock-screen defaults and global hardware gestures;
- silent screen context and capture;
- cross-app input and task control;
- secure settings and other signature/privileged operations;
- tamper-resistant confirmation, policy, and audit storage;
- stronger direct-boot and always-available execution guarantees.

Public Android roles or APIs can cover some phone, SMS, notification, calendar,
contact, and launcher features. Their availability must be represented as
capabilities of the active gateway, never inferred from the package name.

## Extraction Sequence

Keep this work in reviewable follow-up changes:

1. Route runtime and adaptive-surface execution through `PhoneToolGateway`.
2. Split `FrameworkToolExecutor` into public-Android tools and OS-only tools,
with both implementations preserving the same result contract.
3. Move settings and durable stores behind app-owned configuration/storage
interfaces instead of reading arbitrary `Settings.Secure` keys in portable
packages.
4. Add separate app manifests and build targets: a public-SDK Play profile and
the current platform-signed `system_ext` profile.
5. Add a machine-readable capability matrix and run the same runtime/surface
contract suite against both profiles.

Until those steps are complete, changes must keep the existing OpenPhone OS
behavior green and must not weaken framework confirmation or audit paths to
make the app profile easier to implement.
Loading
Loading