oc-ping
v0.0.1
Published
OpenCode V2 iMessage notifications and permission/question replies powered by Photon.
Maintainers
Readme
oc-ping
An OpenCode V2 plugin that sends Photon iMessages when an agent finishes, needs permission, or asks a question. Reply in Messages to resolve permissions and questions. Bring your own Photon credentials.
Setup
Create a project in the Photon dashboard. Its Settings page contains your project ID and secret.
Make these environment variables available to the OpenCode server process. This is the recommended approach because credentials will not be stored in
opencode.jsoncor accidentally committed to a public repository:export SPECTRUM_PROJECT_ID="your-project-id" export SPECTRUM_PROJECT_SECRET="your-project-secret"An already-running background service will not inherit newly exported shell variables. Configure its launch environment and restart it as needed.
Install the dependencies in this directory with
npm install.Add the plugin to your OpenCode configuration, using an absolute path to this directory (copy
opencode.example.jsonctoopencode.jsoncfor local testing; the local file is intentionally gitignored):{ "$schema": "https://opencode.ai/config.json", "plugins": [ { "package": "/absolute/path/to/oc-ping", "options": { "recipient": "+15551234567", "deviceName": "Work MacBook", "events": ["result", "permission", "question"], "replies": true } } ] }For local testing from this repository, copy the example, set
OC_PING_RECIPIENTand the Photon environment variables above, then start OpenCode here:opencodeThe example config loads
.as the plugin package. It enables result, permission, and question notifications with replies. Empty option fields fall back toOC_PING_RECIPIENT,SPECTRUM_PROJECT_ID, andSPECTRUM_PROJECT_SECRETwhen set. You can edit the event selection or adddeviceNamethere.You can put
recipient,projectId, andprojectSecretdirectly in the plugin options for a local-only setup, but never publish or commit that configuration publicly. Keepopencode.jsoncgitignored and use environment variables whenever possible. If credentials are ever committed or shared, rotate the Photon secret immediately.
Use an iMessage-capable phone number or email. The plugin registers only Photon's cloud iMessage provider. Photon manages the sending number; it is not your personal Messages account. Its Free/Pro plans use shared numbers, while Business supports dedicated lines. Photon documents SMS/RCS fallback at the platform level; the plugin rejects recipients explicitly reported as SMS/RCS but does not override Photon transport fallback policy.
Photon receives replies over the SDK's outbound gRPC connection. No public webhook or tunnel is needed. The machine running OpenCode must stay online.
Notifications and replies
| Selection | Behavior |
| --- | --- |
| result | Successful session completion with the last assistant's text result |
| permission | Action, resources, reason, and a reply code |
| question | V2 form title, fields, choices, and a reply code |
| Any exact V2 public event name | A generic notification containing the event name and session ID when present |
Examples of exact names: session.idle, permission.asked, form.created.
The last two use the same actionable formatting as their friendly aliases.
An empty events array disables all notifications. High-frequency events can
produce many messages; there is no wildcard subscription option.
Messages are labeled with the session title. Only sessions at the plugin
instance's directory/workspace are included. Subagent sessions are excluded by
default; set includeSubagents: true to include them.
Optionally set deviceName to a non-empty string to identify the machine sending
messages. It prefixes notifications and reply confirmations, for example:
[Work MacBook] [Fix checkout] Done
Updated the checkout flow.Leading and trailing whitespace is trimmed. Omit deviceName to send messages
without a device label. The label is for display; request codes still route replies.
Permissions
[Fix checkout] Permission needed
shell
npm install
Reply: 8a21b0cd allow | 8a21b0cd always | 8a21b0cd denyallow approves once. always uses OpenCode's saved-permission behavior.
deny rejects. A bare yes or allow without the code does not approve anything.
Questions
For one field, use the code followed by your answer:
8a21b0cd PostgreSQLUse the displayed option value, rather than its label, for choices. For
multi-select fields, separate values with commas. Boolean fields accept
yes/no or true/false. For multiple fields, send a JSON object:
8a21b0cd {"database":"pg","replicas":2}Cancel a question with 8a21b0cd /cancel. External/browser-only fields must be
completed in OpenCode. OpenCode validates field constraints and conditional
requirements before accepting the answer.
Replies are accepted only from the configured recipient in the matching direct conversation and sender line. Codes expire after 24 hours. Requests and deduplication markers use OpenCode's durable plugin storage; pending state is checked against OpenCode before applying a reply. Replies never become arbitrary agent prompts.
V2 form API connection
The targeted V2 plugin context exposes permission APIs but does not expose the
form API. For questions, oc-ping uses @opencode/client and discovers the
authenticated local background service without starting one.
If using a standalone/custom server, set options.serverUrl to that same server
and, if it uses bearer authentication, provide OC_PING_OPENCODE_TOKEN in the
plugin process environment. Other authentication schemes are not implemented.
Never point this setting at another OpenCode instance. Permission replies use
the plugin context directly. Set replies: false for notification-only use.
For multiple dedicated Photon lines, set options.senderPhone to pin routing.
Development and status
npm install
npm run check
npm test
npm pack --dry-runTargets @opencode/plugin and @opencode/client 0.0.0-beta-19378, and
Photon spectrum-ts 12.8.0. These are pinned because the V2 API is beta.
This is an MVP, with type checking and reply-protocol tests. Live Photon delivery
and OpenCode round-trip verification require your configured running service and
Photon credentials. To verify, run a session to completion, trigger an ask
permission and a question, then reply using each generated code. Also try resolving
a request in OpenCode before replying to its message.
Current operational limits:
- OpenCode's event stream is live-only. The plugin reconnects, but events missed while disconnected are not replayed or reconciled.
- Failed sends are logged, not durably queued/retried. Successful sends are deduplicated; a crash between sending and storing the marker can duplicate a notification. Long messages are split into 3,000-character chunks.
- Photon manages its underlying connections. If its top-level receive stream terminates, the plugin logs this and needs a reload.
- Durable deduplication records currently have no automatic retention cleanup.
- Use one configured instance per directory/recipient. Multiple OpenCode server processes sharing the same Photon project are not coordinated by this MVP.
