Quo Internal Comments Skill

What this is

The authoritative skill for Aurora posting internal thread comments in Quo (OpenPhone). Covers the full lifecycle: how a comment is triggered, how it’s formatted, how it’s posted, and what constitutes a correct internal communication.

Core infrastructure (already live)

ComponentPathPurpose
Shared poster libworkspace/scripts/lib/quo-post-comment.jsSingle source of truth for HTTP POST to Quo. All agents import this.
Orchestrator tooltools/quo-thread-orchestrator/tools/reply-to-thread.jsWraps quo-post-comment; called by runner.js; handles R1 fallback
Runnertools/quo-thread-orchestrator/runner.jsLLM loop (Kimi/Moonshot via Portkey). Takes a mentionRow, runs tool loop, posts reply
Authworkspace/scripts/lib/quo-token.jsAuth0 Bearer token from saved browser state. Auto-refreshes.
API endpointPOST https://communication.openphoneapi.com/v2/commentQuo’s internal communication service (not the public OpenPhone API)

Trigger modes

ModeSourcementionRow.proactive_trigger
ReactiveTeam member @mentions Aurora in a threadfalse
ProactiveInbound SMS arrives on a watched linetrue (live 2026-05-04)

Posting rules

  1. Always thread under a specific activityactivityId = mentionRow.parent_activity_id. Never post a floating conversation-level note.
  2. One comment per activity per run — idempotency key prevents duplicate posts on retry.
  3. No em-dashesreply-to-thread.js scrubs ,. Kimi occasionally outputs them.
  4. No raw JSON in notes — runner’s auto_reply_from_end_turn strips code fences before posting.
  5. No SMS fallback — if R3 fails and R1 fallback also fails, the note is NOT sent as SMS. Error logged only.

Thread resolution

Tool: quo_resolve_thread in tools/quo-thread-orchestrator/tools/quo-resolve-thread.js

OpenPhone “Done” = snooze with sentinel duration 525949200s (~16.6 yr). Endpoint:

  • Resolve: PUT https://communication.openphoneapi.com/v2/conversation/{id}/snooze { duration: 525949200 }
  • Unresolve: PUT https://communication.openphoneapi.com/v2/conversation/{id}/unsnooze {}

Aurora can call quo_resolve_thread from the orchestrator tool loop. Updates openphone_mentions.state = 'closed'.

Pending work

  • Communication standards: define what Aurora says in each trigger scenario (reactive @mention, proactive inbound SMS, proactive outbound SMS)
  • Filtering: which lines/conversations should trigger proactive notes vs. skip (currently all inbound on Aurora’s watched lines)
  • Token expiry monitoring: alert if Auth0 refresh fails so R3 doesn’t silently degrade to R1 permanently

Retired paths (do not use)

ScriptRetired reason
openphone-login-and-note.jsPlaywright browser automation — superseded by R3
mac-post-bridge.jsSSH + Playwright to AWS Mac — R1 fallback only, auto-triggered on R3 failure
openphone-internal-comment.jsFile-queue workaround — obsolete since R3 discovered
quo-browser-automation.jsBrowser automation — superseded by R3
quo-watcher-c1/c2/c3/c5-*.jsBakeoff contestants — C4 headless WS won
quo-response-discovery.jsNetwork capture probe — endpoint found, retired
quo-bakeoff-analyze.jsBakeoff analytics — bakeoff complete

All above files moved to workspace/scripts/_retired/quo-bakeoff-2026-04/ and workspace/scripts/workers/_retired/.

How to invoke (manual test)

# Dry run — see what Aurora would post without sending
NODE_PATH=.../node_modules DRY_RUN=true node workspace/scripts/tests/test-quo-proactive-notes.js --msg=1
 
# Live — post actual note to test conversation
NODE_PATH=.../node_modules DRY_RUN=false node workspace/scripts/tests/test-quo-proactive-notes.js --msg=1
 
# Direct API test
node -e "
const { postComment } = require('./workspace/scripts/lib/quo-post-comment');
postComment('AC_ACTIVITY_ID', 'Test note').then(r => console.log('posted:', r)).catch(e => console.error(e.message));
"

References

  • KB: workspace/knowledge-base/openphone/API.md — endpoint spec, auth, body shape
  • Test conversation: https://my.quo.com/inbox/PN4GfD7RX2/c/CN301c8943c2174ac68d11550bfaf6cc19
  • Skill trigger registry: CLAUDE.md Tool Trigger Conditions table