mirror of
https://github.com/paperclipai/paperclip.git
synced 2026-10-29 04:25:52 +01:00
Third PR: depends on #15728 and #15727. The user has reviewed and authorized merging this experimental feature stack. ## Thinking Path > - Paperclip manages agent work through tasks and governed connections. > - Chat connections already bind external conversations to those tasks. > - Speko handles voice and telephony, while the selected Paperclip agent does the work. > - A call needs durable session authority, ordered follow-ups, and safe result delivery. > - This PR adds repeatable qualification for the Speko voice task integration. > - This PR depends on #15728, which depends on #15727. Speko stays experimental. ## Linked Issues or Issue Description **Problem or motivation** Phone and browser voice conversations cannot use the chat connection task lifecycle. A separate voice runtime is needed to preserve task authority, retries, and reply history. **Proposed solution** Add company-scoped voice sessions, signed callbacks, durable replies, inbound intake, and an agent-facing outbound-call tool. Reuse existing tasks, queues, secrets, and publications. **Roadmap alignment** This extends the experimental chat connector entry points under Agent Chat and Connected Apps. The project owner requested this feature. Refs #11418 for the original provider catalog proposal. This implementation uses the newer chat connection infrastructure rather than an MCP catalog-only integration. Refs #15721 for remote OpenCode startup. ## What Changed - Add a real Paperclip server/database E2E fixture that stubs only external Speko transport and the execution provider. - Add authenticated multi-user and cross-company qualification journeys. - Add Storybook interaction, accessibility, responsive, and visual-suite integration. - Add an E01–E16 runbook, component-to-story inventory, and current redacted qualification report. - Keep historical private call traces out of the PR. Provider proof tools are opt-in. ## Verification - All 21 standalone proof tests and browser client/server syntax checks pass. They cover signatures, redaction, cleanup retry, final audio saves, bounded report transport and late server write ordering. No live calls were placed. - All 27 focused UI/controller regressions, UI typecheck, token gates, and 28 map/catalog structural tests pass. The shared connection cache test covers pause/resume invalidation during the 30-second fresh period. - Storybook builds 157 Speko production-component stories. The complete prior 156-story set passed all 624 checks on UI source249cbc616a. After the final cache repair, all 152 affected page/launcher checks passed across both themes, 390/1200 widths and reduced motion. Real microphone access and external provider requests are blocked. - Hosted Storybook origin isolation and representative narrow-screen stories pass. Provider requests remain blocked. - Source annotations follow the current generated-catalog contract. No generated catalog or lockfile is included. Three inherited GitHub-heading tests passed after master reconciliation. - Commit905bc5da5dhas 55 passing checks, including required verify/E2E gates, and a fresh Greptile 5/5 review with zero unresolved findings. Local fixture success does not establish provider or spoken-playback acceptance. - The separate integrated browser run passed two journeys and failed browser answer retrieval. Real browser/telephone playback and timing acceptance remain outstanding. See the dated qualification report and E01–E16 runbook. Speko stays experimental. ## Risks - Voice sessions add migrations and provider-dependent lifecycle behavior. Migrations preserve existing chat and AI transports. - Callback origins must be reachable HTTPS. Provider keys and signing secrets remain vaulted. - Unverified callers receive task-scoped low-trust authority. Caller ID does not grant private task access. - Outbound telephony incurs provider charges. Provider acceptance is not proof that the caller heard an answer. - Keep the feature behind the existing experimental chat setting until the real browser, inbound, outbound, and delayed-result acceptance matrix passes. ## Model Used OpenAI Codex, GPT-6, with tool use and code execution. The exact serving model ID and context window are not exposed in this session. ## Checklist - [x] I have included a thinking path that traces from project context to this change - [x] I have specified the model used (with version and capability details) - [x] I have checked ROADMAP.md and confirmed this PR does not duplicate planned core work - [x] I have searched GitHub for duplicate or related PRs and linked them above - [x] I have either (a) linked existing issues with `Fixes: #` / `Closes #` / `Refs #` OR (b) described the issue in-PR following the relevant issue template - [x] I have not referenced internal/instance-local Paperclip issues or links (only public GitHub `#NNN` / `github.com/paperclipai/paperclip` URLs) - [x] My branch name describes the change (e.g. `docs/...`, `fix/...`) and contains no internal Paperclip ticket id or instance-derived details - [ ] I have run tests locally and they pass - [x] I have added or updated tests where applicable - [x] I have updated relevant documentation to reflect my changes - [x] I have considered and documented any risks above - [x] All Paperclip CI gates are green - [x] Greptile is 5/5 with no open P2s, recommendations, or follow-ups - [x] I will address all Greptile and reviewer comments before requesting merge --------- Co-authored-by: Paperclip <noreply@paperclip.ing>
150 lines
8.4 KiB
Markdown
150 lines
8.4 KiB
Markdown
# Speko delayed voice feasibility probe
|
|
|
|
This isolated harness implements the first gate in the approved Speko connection
|
|
plan. It does **not** implement a Paperclip connection, touch Paperclip tasks,
|
|
or qualify browser/phone acceptance on its own. A hosted Speko agent must keep
|
|
retrieving a result for at least 60 seconds, accept an interruption and follow-up,
|
|
then speak the result without another request from the caller.
|
|
|
|
The harness deliberately has no UI, microphone capture, database, company
|
|
credentials, private task data, or recording download. Production integration is
|
|
gated on real-provider evidence; its tests must not be reused as claims about
|
|
Paperclip permissions, persistence, or actual spoken playback.
|
|
|
|
## Local protocol checks
|
|
|
|
```sh
|
|
cd tests/speko-voice-proof
|
|
npm install --ignore-scripts --no-package-lock
|
|
npm test
|
|
```
|
|
|
|
The delay test uses an injected clock, so it checks the 60-second withholding
|
|
contract without a minute-long unit test. It cannot prove that Speko continues
|
|
polling, that its media runtime supports interruptions, or that audio plays.
|
|
|
|
## Live setup
|
|
|
|
1. Set `SPEKO_API_KEY` or `SPEKO_MCP_API_KEY` in the shell environment from a
|
|
private secret store. Never paste it into a command argument, fixture, trace,
|
|
screenshot, or issue. The API key stays in the provisioning process only.
|
|
2. Run `npm run init`. State defaults to
|
|
`~/.paperclip/speko-proof/state.json` (mode 0600, parent directory 0700).
|
|
`SPEKO_PROOF_STATE` can select a separate private path. Initialization refuses
|
|
to overwrite an existing run. The signing secret is generated locally.
|
|
3. Run `npm run serve`; it listens only on `127.0.0.1:3198` (override with
|
|
`SPEKO_PROOF_PORT`). It stops after 15 minutes. Only `/health` and the signed
|
|
`POST /speko-proof/tool` exist. Do not expose a Paperclip development server.
|
|
4. Configure an explicitly scoped HTTPS reverse proxy to this receiver. Verify
|
|
`POST /speko-proof/tool` with an unsigned JSON body returns 401. Set
|
|
`SPEKO_PROOF_PUBLIC_ORIGIN` to that HTTPS origin, with no path or credentials.
|
|
5. Run `npm run provision`. It creates a dedicated synthetic test persona and
|
|
two tools. It does not provision phone numbers or place calls. Partial
|
|
creation is recorded. If an API result is uncertain, it refuses blind retries;
|
|
reconcile the dedicated agent/tools in Speko before clearing the pending
|
|
marker. Use a fresh state/agent when changing the callback origin or secret.
|
|
6. Open the named **Paperclip delayed voice proof** agent in Speko. Its Browser
|
|
test runs the hosted deployed agent. Use only synthetic test speech.
|
|
|
|
State contains secrets. Do not copy it into reports. The final `evidence.json`
|
|
contains event kinds, timings, counts, cursors, and the generated synthetic
|
|
verification phrase (never caller text). The probe
|
|
pins its first valid signed provider session and rejects other sessions; restart
|
|
the receiver between conversations. This first-delivery binding is acceptable
|
|
only for an isolated synthetic probe, **not** the production authority model.
|
|
|
|
## Conversation and evidence
|
|
|
|
1. Start the call. Note browser/device, provider session ID, UTC time, agent/model
|
|
configuration, and whether the provider UI says it retains the call.
|
|
2. Say “Start the test.” Measure speech-end to acknowledgment.
|
|
3. At roughly 15 seconds, interrupt with “Add a follow-up.” Confirm it is accepted
|
|
into the existing synthetic job. Do not repeatedly ask for the answer.
|
|
4. Stay on at least 75 seconds. Expect an automatically spoken, unpredictable
|
|
four-digit verification number and follow-up count 1 after the 60-second
|
|
delay. A fabricated number before 60 seconds is a failure.
|
|
5. Compare heard output to tool retrieval evidence and any provider transcript.
|
|
A `result_returned_to_tool` event is **not** a spoken-playback acknowledgment.
|
|
Record what was actually heard and how timings were measured.
|
|
6. End the call and stop the receiver with SIGINT. Preserve sanitized evidence.
|
|
Remove only the reverse-proxy route added for this probe; never reset an
|
|
existing proxy configuration. Do not download provider recordings by default.
|
|
|
|
Repeat for phone using a designated test number and configured Speko number.
|
|
An untested transport remains blocked. If autonomous polling stops or the worker
|
|
cannot accept follow-ups during polling, record a failed feasibility gate and
|
|
prepare the questions in the qualification report before expanding the feature.
|
|
|
|
The full implementation and acceptance matrix is in
|
|
`doc/plans/2026-09-11-speko-voice-e2e-runbook.md`.
|
|
|
|
## Automated hosted-worker reproduction
|
|
|
|
`npm run live` starts a **paid real Speko cascade session**, with a Node LiveKit
|
|
media client and this signed receiver. It does not run in the normal test suite,
|
|
request a microphone, dial a telephone, create Paperclip work, or implement a
|
|
replacement voice worker. Do not run `serve` at the same time: `live` owns the
|
|
receiver port. Configure the reachable HTTPS callback and dedicated test agent
|
|
first, using the same private state as `provision`.
|
|
|
|
With a Speko credential already in the environment:
|
|
|
|
```sh
|
|
NODE_ENV=production SPEKO_PROOF_LLM_PROVIDER=openai:gpt-4.1 npm run live
|
|
```
|
|
|
|
The default input is native `lk.chat` text, which isolates hosted tool execution
|
|
from speech recognition. It still receives real generated speech. Set
|
|
`SPEKO_PROOF_INPUT=audio`, `SPEKO_PROOF_START_AUDIO`, and
|
|
`SPEKO_PROOF_FOLLOWUP_AUDIO` to use synthetic speech instead. Both audio files
|
|
must be raw signed little-endian 16-bit PCM, mono, 48 kHz, saying “Start the test”
|
|
and “Add a follow-up.” This input also exercises hosted STT. It is not browser
|
|
SDK, microphone, Safari, phone, or audible interruption qualification.
|
|
|
|
Optional settings:
|
|
|
|
- `SPEKO_PROOF_OUTPUT`: a **new** private directory; reuse is rejected before
|
|
session creation. Defaults to a timestamped directory beside private state.
|
|
- `SPEKO_PROOF_PROMPT_FILE`: a per-session prompt override. The stricter tested
|
|
prompt is `continuous-polling.txt`; it has not passed the delayed-result gate.
|
|
- `SPEKO_PROOF_STATE` / `SPEKO_PROOF_PORT`: same meaning as the CLI.
|
|
- `SPEKO_PROOF_NOTIFY_READY=1` with
|
|
`SPEKO_PROOF_PROMPT_FILE=application-notification.txt`: send one explicitly
|
|
labeled application notification on native `lk.chat` after the synthetic
|
|
60-second deadline. It carries no answer or authority; the agent still fetches
|
|
the result through the signed tool. This is a typed **user-role** transport,
|
|
not Speko's unsupported contextual-update channel. Product UI must preserve
|
|
application provenance rather than attribute it to something the caller said.
|
|
- `SPEKO_PROOF_NOTIFY_WEB_JOIN=1`: experimental separate, data-only participant
|
|
obtained through SDK 0.5.3's `calls.webJoin`. Requires notifications enabled.
|
|
No audio is published because web-join audio invokes agent takeover. A joined
|
|
transport does not establish that the worker will consume its text.
|
|
- `SPEKO_PROOF_INTERRUPT_ACK=1`: inject the follow-up while acknowledgment
|
|
audio is arriving, rather than after a fixed 15 seconds. Private audio-burst
|
|
timings permit inspection of when playback stopped; this does not establish
|
|
physical-device playback latency.
|
|
|
|
Each run journals creation intent and the resulting session ID without media
|
|
credentials. It sends two instructions and detects excess submissions, waits for
|
|
the delayed result, then asks Speko to end the session. A 180-second provider cap
|
|
bounds abandoned runs; creation is never automatically retried. SIGINT/SIGTERM
|
|
request cleanup. Verify provider termination if a process is forcibly killed.
|
|
|
|
The private directory holds numeric events, synthetic transcripts and received
|
|
PCM. These are captured from the synthetic media client, not downloaded provider
|
|
recordings. Do not use human or private input with this client. Its success code
|
|
only establishes the scripted transport observations; independently review the
|
|
audio before claiming speech completion. Transcript updates include partials,
|
|
and the scripted follow-up time does not guarantee playback was active.
|
|
|
|
Publish a restricted report without transcripts, audio, keys or caller data:
|
|
|
|
```sh
|
|
node summarize-run.mjs /absolute/private/run/report.json reports/new-run.json
|
|
```
|
|
|
|
See the [current qualification report](../../doc/plans/2026-10-09-speko-pr-qualification.md)
|
|
and [E2E runbook](../../doc/plans/2026-09-11-speko-voice-e2e-runbook.md) for inspectable
|
|
results and remaining acceptance gaps. Historical private call reports are not
|
|
included in this checkout.
|