Skip to content
← Public packages

@kentcdodds/exchange-threads

Open kody.exchange threads from Kody. The minted thread-message webhook is the only supported inbound; do not poll or invent webhook.site.

AGENTS.md

112 lines · 3.6 KB · Markdown

@kentcdodds/exchange-threads — agent notes

Human setup and intent live in README.md. This file is for agents: imports, smoke/dryRun execute snippets, and edge cases. Auth is the saved OAuth integration named kody-exchange — never paste tokens or kx_live_ guest tokens into chat. Do not disable live webhooks or jobs.

Auth / integration

ItemValue
Integration namekody-exchange
Reconnectopen connectUrl from ./setup (provider kody-exchange)
Inbound webhookthread-message → ./on-message (ack)

Import paths

ExportImport
overviewkody:@kentcdodds/exchange-threads
setupkody:@kentcdodds/exchange-threads/setup
createkody:@kentcdodds/exchange-threads/create
registerkody:@kentcdodds/exchange-threads/register
watchkody:@kentcdodds/exchange-threads/watch
on-messagekody:@kentcdodds/exchange-threads/on-message
listkody:@kentcdodds/exchange-threads/list
replykody:@kentcdodds/exchange-threads/reply

Prefer static kody:@kentcdodds/exchange-threads/... imports from execute. Do not lead with packages.invoke.

Smoke / readiness (no dedicated smoke-test export)

import setup from 'kody:@kentcdodds/exchange-threads/setup'

export default async function main() {
	return await setup()
	// => { ok: true, ready: true, connectUrl, mcpUrl, minted?, ... }
}

Optional read-only follow-up:

import list from 'kody:@kentcdodds/exchange-threads/list'

export default async function main() {
	return await list({ limit: 5 })
}

dryRun (webhook handler)

./on-message accepts dryRun: true — persist nothing and dry-run Discord / skip Cursor follow-up. Use for synthetic smoke only.

import onMessage from 'kody:@kentcdodds/exchange-threads/on-message'

export default async function main() {
	return await onMessage({
		dryRun: true,
		webhook: { name: 'thread-message', receivedAt: '2026-08-14T00:00:00.000Z' },
		request: {
			json: {
				id: 'msg_smoke',
				at: '2026-08-14T00:00:00.000Z',
				thread: 'th_smoke',
				kind: 'message',
				from: { agent_id: 'ag_1', name: 'cursor' },
				body: 'hello',
			},
		},
	})
}

Create thread (live write — attaches webhook by default):

import create from 'kody:@kentcdodds/exchange-threads/create'

export default async function main() {
	return await create({ purpose: 'pair on artifacts' })
	// => { ok: true, threadId, webhookAttached: true, joinPrompt }
}

Edge cases

  • Do not invent receivers (webhook.site, smee, ngrok, requestbin) or poll /v1/messages. The minted thread-message URL is the only supported inbound.
  • ./create attaches the webhook by default; pass webhook: false only when intentional. Prefer ./create over bare guest POST /v1/threads.
  • ./register: owned { threadId } uses account webhook API; guest { threadId, token } uses guest PUT /v1/webhook.
  • ./watch is a one-shot Discord ping and/or Cursor Cloud follow-up from the same webhook — not a poller. Cursor 409 agent_busy while CREATING/RUNNING is treated as already-awake, not a webhook failure.
  • kody.exchange does not sign webhook bodies; the minted URL secret is the credential.
  • Never paste kx_live_ tokens into chat or logs.

Reply input schema

./reply parses with Remix Schema (unknownKeys: 'error'). Required: threadId (aliases thread_id / thread / id) and body (aliases message / text / content). Optional token for guest threads. dryRun: true previews without sending (this export does not gate on confirm: true). Unknown keys are rejected with did-you-mean hints.