> ## Documentation Index
> Fetch the complete documentation index at: https://docs.drdj.me/llms.txt
> Use this file to discover all available pages before exploring further.

# DealDash Agent Bridge Plugin

> Provider-neutral MCP bridge for safe DealDash agent actions.

`dealdash-agent-bridge` is the single repo-local plugin that gives agents a safe DealDash tool surface. It is designed for Claude Code, OpenAI/Codex or ChatGPT MCP clients, Hermes, OpenClaw, and future agent hosts.

OpenClaw is treated as an optional WhatsApp and Telegram adapter, not as the core identity of the system.

Human operators should start with [Connect An AI Agent](../agents/agent-quick-start.mdx) or the in-app `/tools/ai-agent-setup` page. This page is the agent-optimized reference for exact tool names, auth headers, and safety rules.

## One Plugin Model

There is one active DealDash agent path:

1. `dealdash-agent-bridge` MCP tools, when the host has MCP configured.
2. Direct HTTPS calls to the same `/api/agent/*` backend, when MCP is not live but the host can make HTTPS requests.

The private DealDash Agent Kit is a sanitized install/example package for agents. It is not a separate runtime and should not be treated as a blocker when the repo-local plugin or direct HTTPS backend is available.

Agents must not answer "install MCP first" for normal DealDash tasks unless the host has neither MCP tools nor HTTPS access. For LinkShot view-check imports, missing MCP is not a reason to request Telegram approval or switch to generic deal create/update.

## Architecture

1. DealDash agent core: canonical `/api/agent/*` backend contract with acting-user scoped access.
2. MCP server: provider-neutral stdio server in `/plugins/dealdash-agent-bridge/scripts/server.ts`.
3. Channel adapters: optional messaging integrations that normalize attachments, capture approvals, and apply channel tone.

## Backend Auth

Normal user setup uses login-link auth:

1. The MCP bridge calls `POST /api/agent/auth/start`.
2. The agent sends the returned DealDash approval link to the user.
3. The user opens the link, signs in, and approves.
4. The bridge polls `GET /api/agent/auth/status/:requestId?deviceCode=...`.
5. After approval, the bridge stores the scoped token inside the agent host or MCP process.

If the agent does not yet have the MCP bridge installed but can make HTTPS
requests, it may start the same flow directly:

```bash theme={null}
curl -fsS -X POST https://dealdash.tech/api/agent/auth/start \
  -H 'content-type: application/json' \
  --data '{"actorId":"openclaw-agent","channel":"openclaw:setup"}'
```

The agent should send only `auth.authorizeUrl` to the user, keep
`auth.deviceCode` and any approved token inside its secure tool/session state, and
poll `auth.statusEndpoint` after the user approves. A normal user setup must not
be blocked on `DEALDASH_AGENT_SERVICE_SECRET`, `DEALDASH_AGENT_DEFAULT_USER_ID`,
or deployment provider settings.

After login-link auth, direct HTTPS clients call `/api/agent/*` with the stored
login token:

```bash theme={null}
curl -fsS -X POST https://dealdash.tech/api/agent/deals/bulk-import/process \
  -H "authorization: Bearer <stored-login-link-agent-token>" \
  -H "content-type: application/json" \
  -H "x-dd-channel: openclaw:telegram" \
  -H "x-dd-actor-id: openclaw-agent" \
  -H "x-dd-request-id: <unique-request-id>" \
  --data '{"postDate":"2026-04-13","linksText":"https://www.tiktok.com/@creator/video/1"}'
```

The token stays inside the agent tool/session state. Never ask the user to paste
it into chat. Login-link tokens are long-lived and renewable, defaulting to 30
days with a server cap of 365 days via `DEALDASH_AGENT_TOKEN_TTL_MS`.
Operators can inspect and revoke active persistent connections from `/settings`
-> **AI Agent Connection**, which clears stored token material without deleting
the audit row.

OAuth-capable public agents may use the device-flow wrapper instead of the
legacy login-link endpoints:

```bash theme={null}
curl -fsS -X POST https://dealdash.tech/api/agent/oauth/register \
  -H 'content-type: application/json' \
  --data '{"client_name":"My DealDash Agent"}'

curl -fsS -X POST https://dealdash.tech/api/agent/oauth/device_authorization \
  -H 'content-type: application/json' \
  --data '{"client_id":"<registered-client-id>"}'

curl -fsS -X POST https://dealdash.tech/api/agent/oauth/token \
  -H 'content-type: application/json' \
  --data '{"grant_type":"urn:ietf:params:oauth:grant-type:device_code","device_code":"<device-code>","client_id":"<registered-client-id>"}'
```

Public OAuth client registration issues no client secret. Device-code token
states map to standard OAuth errors: `authorization_pending`, `access_denied`,
`expired_token`, and `invalid_grant`.

OAuth scopes are allowlisted. New full-bridge clients default to
`dealdash.read dealdash.write.low_risk dealdash.memory`; clients that request
unknown scopes are rejected with `invalid_scope`. Read tools require
`dealdash.read`, Tier 1 operational writes require `dealdash.write.low_risk`,
and sensitive routes require `dealdash.write.sensitive` or `mcp` plus the
existing approval gate. The approval page shows the stored request snapshot:
client name, client URL, requested scopes, expiration, and a warning when `mcp`
requests full bridge access.

If `agent_auth_status` reports `connectionState: "token_already_claimed"`
without returning a new stored token, the approval link was already claimed by a
different agent process. Start a fresh login-link authorization instead of
claiming that the current session is connected.

Operator-managed adapters can still use gateway-delegated authentication.

* `Authorization: Bearer <DEALDASH_AGENT_SERVICE_SECRET>`
* `x-dd-acting-user-id`
* `x-dd-channel`
* `x-dd-actor-id`
* `x-dd-request-id`
* `x-dd-approval-id` for sensitive writes

The backend enforces acting-user scope before any proxied app route runs. Login
approval links are generated from `APP_ORIGIN` first, then deployment fallback
origins if needed; the short-link domain is not the preferred approval origin.

Operators can set
`DEALDASH_AGENT_WRITE_CONFIRMATION_MODE=allow_non_delete_writes` when an agent
should run non-delete write tools without repeated approval prompts. Delete
operations always require approval confirmation, regardless of this mode.

Operators can verify the safe bring-your-own-agent surface inside DealDash at `/settings` -> **AI Agent Connection**. The Settings card shows connection status, active persistent connections, token last-four and expiry timestamps, `.mcp.json` path, MCP command, login-link endpoints, permission tiers, tool families, revocation controls, and recent scoped logs without exposing secret values.

The private DealDash Agent Kit repository is [djasha/dealdash-agent-kit](https://github.com/djasha/dealdash-agent-kit). It contains agent Markdown instructions, skill files, and MCP setup notes for agents. It must never contain real secrets, user data, production tokens, database URLs, or private local paths.

## Public Discovery

Agents should discover the bridge from the live website before assuming route
shape:

* API catalog: `https://dealdash.tech/.well-known/api-catalog`
* OpenAPI description: `https://dealdash.tech/openapi.json`
* OAuth authorization server: `https://dealdash.tech/.well-known/oauth-authorization-server`
* OAuth protected resource: `https://dealdash.tech/.well-known/oauth-protected-resource`
* MCP server card: `https://dealdash.tech/.well-known/mcp/server-card.json`
* Shortener-only MCP server card: `https://drdj.me/.well-known/mcp/drdj-shortener/server-card.json`
* A2A Agent Card: `https://dealdash.tech/.well-known/agent-card.json`
* Agent Skills index: `https://dealdash.tech/.well-known/agent-skills/index.json`
* Skill Markdown: `https://dealdash.tech/.well-known/agent-skills/dealdash-agent-bridge/SKILL.md`
* Shortener Skill Markdown: `https://drdj.me/.well-known/agent-skills/drdj-shortener/SKILL.md`
* Web Bot Auth key directory: `https://dealdash.tech/.well-known/http-message-signatures-directory`

Unauthenticated `/api/agent/*` responses include a `WWW-Authenticate` challenge
with `resource_metadata` pointing at the OAuth protected-resource metadata.
The OpenAPI document also repeats those OAuth discovery URLs as `x-oauth-*`
fields on the root object and bearer auth scheme for clients that bootstrap
from OpenAPI first.
Public marketing pages support `Accept: text/markdown` for compact reading.
Browser agents can use WebMCP only for public bootstrap tools: schema fetch,
public OAuth registration/device authorization, public Markdown reads, and
canonical setup/documentation links. Browser WebMCP does not expose account
read/write tools in V1.

Run `pnpm agent:mcp-install:check --json` before copying setup instructions or
refreshing a local Codex plugin install. The check verifies the repo MCP config,
boots the configured server without tokens, and, when a local Codex plugin cache
exists, fails if that cache is stale, missing current tools such as
`linkshot_evidence`, or missing the skill's required
`references/userflows.md`. After a cache refresh, start a new Codex session or
reload the agent host before validating deferred tool discovery; existing
conversations can keep the old tool list.

For live host-matrix proof under `DJA-207`, capture the host's current
`agent_schema` result as JSON after reloading or starting a new Hermes, Codex,
or Claude Code session, then run:

```bash theme={null}
pnpm agent:host-matrix:check --host <host-label> --agent-schema-json <path> --json
```

The command is no-write. It validates the current `agent_schema.hosts` matrix,
the read-first LinkShot startup actions, and the required `linkshot_evidence`
planning/readiness actions. It refuses to produce a passing Linear-ready proof
when the supplied capture includes secret-shaped fields such as tokens, device
codes, service secrets, database URLs, or API keys.

## Host Runtime Profiles

All supported hosts use the same bridge order: MCP tools first, then direct
HTTPS login-link auth to `/api/agent/*`, then stop only when the host has
neither MCP nor HTTPS access.

* Claude Code and OpenAI/Codex can call MCP tools directly when the bridge is
  installed. If a specific session lacks MCP tools but can make HTTPS requests,
  use the direct fallback instead of asking the user to install a second
  DealDash runtime.
* Hermes agents use the same contract. Prefer MCP when the active Hermes
  profile exposes it; otherwise use direct HTTPS login-link auth with a
  Hermes-specific `channel` such as `hermes:desktop`, `hermes:telegram`, or
  `hermes:agent`. Hermes must not ask for DealDash service secrets, acting-user
  IDs, database URLs, or deployment provider settings.
* Codex and Claude Code agents that edit this repo must follow `AGENTS.md`,
  keep changes PR-sized, run the relevant repo checks, and refresh
  `docs/architecture/AI_SITEMAP.md` through the Understand lane only when
  structure changes.
* Messaging adapters such as OpenClaw are channel adapters, not alternate
  DealDash runtimes. Use channel labels such as `openclaw:telegram`,
  `openclaw:whatsapp`, or `hermes:telegram` so audit logs show where the
  request originated.

## Tool Families

* Proxies: list the acting user's saved proxy profiles; returned credentials are secrets
* Short links: create from URL, list, get, stats, delete
* Screenshots: upload photo, capture URL, reserve, link to deal, get, latest, delete
* Deals: search, get, latest, create, update, find by URL, due views, view logs, bulk add view checks, delete video, save view log, update view log, progress
* Payments: list, create, update, delete
* Tasks: list, get, latest, create, complete, reopen
* Contacts: search, get, latest, upsert, by phone, deals, notes, labels, counts, duplicates
* Outreach: stages, summary, campaigns, prospects, Kanban movement, contacts, conversations/messages, evidence, deals/rate cards, follow-ups, approval queue/status/decision, local draft/summary/rate/risk/next-action helpers, Google Sheets mirror export/import, Twenty status, and demo seed
* Influencers: search, get, latest, upsert, by phone, by promo code, stats, last progress
* Activity: feed and analytics
* Suggestions: pipeline summary, next actions, payment followups, content followups
* Templates: message templates, latest story message template, and reusable deal templates
* Context: search and resource hints
* Memory: list, create, archive
* LinkShot evidence: packet from screenshot/candidate, rank search results, rank candidates, summarize screenshot examples, plan video search, plan proof capture, plan direct-proof review, audit browser intake, extract transcript cues, proof-run harness, format verification report, audit workflow readiness, workflow report
* Approvals: request and status

Every MCP tool advertises the complete tool risk annotation vocabulary:
`readOnlyHint`, `destructiveHint`, `idempotentHint`, and `openWorldHint`.
Read-only tools are marked read-only/idempotent. Mixed read/write tools stay
conservative; update, upsert, archive, status-transition, and delete-capable
families set `destructiveHint: true`, and tools that can shorten or capture
external URLs set `openWorldHint: true`.

Successful MCP tool results return the same JSON object in `structuredContent`
and as serialized text content. Keep both surfaces aligned so modern MCP hosts
can parse structured results while older clients can still read the text block.
MCP-safe error results use `isError: true` with JSON text containing `error`,
`code`, `status`, and safe `details`; they must not echo authorization headers,
service secrets, image data, or stored login tokens.

## MCP Resources And Prompts

The MCP server exposes fixed resources for hosts that support `resources/read`:

* `dealdash://context/search`: default search across recent accessible DealDash context.
* `dealdash://linkshot/view-logs`: recent LinkShot View Logs.
* `dealdash://memory/active`: active human-visible agent memory.

The MCP server also exposes prompts:

* `dealdash_read_only_start`: start with context search, screenshots, View Logs, and due views.
* `dealdash_image_to_link`: guide a safe image upload and short-link return flow.
* `dealdash_linkshot_evidence_packet`: normalize screenshot, candidate, view-count, and direct-proof signals before any LinkShot view-log write.
* `dealdash_linkshot_video_proof_search`: guide a `/grillme`-style proof run from read-only context through candidate ranking, proof capture, final report, and readiness audit.

Use resources for context, prompts for guided workflows, and tools for actions.

## Approval Tiers

* Tier 0: reads only
* Tier 1: low-risk writes such as short-link creation, screenshot upload, screenshot linking, LinkShot bulk view-check imports, task state changes, and note creation
* Tier 2: sensitive writes such as direct deal create/update/delete, payment CRUD, contact deletes, screenshot deletes, and visibility changes

By default, Tier 2 calls require an approved `x-dd-approval-id`.
`DEALDASH_AGENT_WRITE_CONFIRMATION_MODE=allow_non_delete_writes` removes that
approval requirement for non-delete Tier 2 writes when the operator explicitly
enables it. Delete operations always require approval. For OpenClaw messaging
flows, Telegram approval evidence should be captured by the adapter before
confirming the approval record. Confirmation is not exposed as a generic MCP
tool and requires `DEALDASH_AGENT_APPROVAL_CONFIRM_SECRET` plus
`x-dd-approval-confirm-secret`.

Agent-routed deal create/update calls persist through the same backend routes as
dashboard edits. Successful writes leave the normal domain activity row with
payload-safe agent metadata (`agentSource`, request id, actor id, channel,
operation, approval tier, and approval id) and a separate payload-safe
`agent_tool_call` row with request metadata plus response-derived deal IDs,
deal name, promo code, status, and LinkShot bulk-import summary IDs/counts when
available. Agent-routed screenshot reserve/upload/link writes add payload-safe
proof handles such as
`screenshotId`, `shortId`, `dealId`, `videoId`, `sourceUrl`, `platform`,
`detectedViews`, `pending`, and `uploaded`, without logging raw image data,
storage paths, public image URLs, or rendered `shortUrl`/`directUrl` values.

## V1 Exclusions

V1 intentionally does not expose admin/auth/team tools, runtime execution, filesystem tools, or bulk destructive operations.

URL screenshot capture accepts only `http` and `https` URLs and blocks local/private targets by default. Use `DEALDASH_AGENT_ALLOW_PRIVATE_CAPTURE_URLS=true` only for trusted development capture.

`capture_url` chooses its browser session and proxy per target host. Instagram/Facebook/YouTube need a logged-in session (`DEALDASH_AGENT_CAPTURE_PROFILE_DIR`, set up with `pnpm agent:capture:setup --login`; or `DEALDASH_AGENT_CAPTURE_CDP_URL` / `DEALDASH_AGENT_CAPTURE_STORAGE_STATE`). TikTok null-routes commercial-proxy/datacenter/VPN ASNs, so it needs a mobile/carrier exit routed via `DEALDASH_AGENT_CAPTURE_PROXY_ROUTES` (JSON `{hostname-substring: proxyUrl}`), with `DEALDASH_AGENT_CAPTURE_PROXY` as the box-wide fallback. BrightData `web_data_tiktok_*` datasets provide TikTok view counts and dates server-side for discovery and cross-check only; the accepted proof is always the live platform screenshot.

Photo uploads accept PNG, JPEG/JPG, WebP, GIF, AVIF, and BMP raster images by default. The MCP bridge enforces `DEALDASH_AGENT_MAX_IMAGE_BYTES` before sending the payload to DealDash. The current default binary image limit is 40MB; the DealDash API JSON body limit has 64MB headroom for base64 encoding. Raise those limits only in controlled deployments because base64 uploads are memory-heavy.

SVG, HEIC, and TIFF are not accepted by default. SVG can contain active content, and HEIC/TIFF are not reliably previewable in browser-based DealDash screenshot galleries. Messaging adapters should convert those formats to PNG/JPEG/WebP before calling `screenshots.upload_photo`.

### Alternate Proof Blob Stores

Alternate proof blob stores such as Chibisafe must stay server-side adapters
behind the DealDash screenshot routes. Do not expose a Chibisafe API key to the
LinkShot extension, browser clients, MCP hosts, Telegram, or logs. Even when a
blob is stored outside Supabase, the bridge response must still include the
DealDash `screenshotId`, `shortUrl`/`directUrl`, source URL, platform, deal/video
IDs when known, and detected view suggestions when available. The external blob
URL is storage evidence only; LinkShot proof completion and persistent view-log
writes still require the full DealDash source/proof/date/platform/views/direct
proof gate.

## LinkShot Evidence Workflow

Use `linkshot_evidence` before any persistent view-log write from searched or
captured video evidence. The safe sequence is:

1. Read current DealDash context with `context.search`, `deals.due_views`,
   `deals.view_logs`, and `screenshots.list_latest`.
2. Summarize prior screenshots with `summarize_screenshot_examples` as capture
   references only.
3. Call `plan_video_search` before browser or platform search.
4. Follow the returned `browserSearchTargets`, gather the
   `candidateIntakeFields`, and fill `rankingInputTemplate` for each raw
   candidate.
5. Call `audit_browser_candidate_intake` before ranking so missing
   video/account/date fields, transcript cue gaps, and direct-proof gaps stay
   visible.
6. When transcript, caption, or first-comment text is available, call
   `extract_transcript_cues` and copy `candidatePatch.transcriptMentionAtSeconds`
   into the candidate as a navigation cue only.
7. Rank raw results with `rank_search_results` or normalized candidates with
   `rank_candidates`.
8. Call `plan_proof_capture` to turn ranked rows into explicit screenshot jobs
   with selector hints, metadata templates, direct-proof state, and missing write
   requirements.
9. Capture or upload the current proof screenshot with `screenshots.capture_url`
   or `screenshots.upload_photo`.
10. Pass current captures as `proofScreenshots`, not historical `screenshots`,
    so the tool matches proof to the correct video URL.
11. Call `plan_direct_proof_review` before claiming confirmation; transcript,
    caption, comment, and link matches stay navigation-only.
12. Call `proof_run_harness` with ranked rows, current `proofScreenshots`, and
    prior view-log/import rows so duplicates, already-existing rows, date
    confidence, verifier status, and write gates are deterministic.
13. Finish with `workflow_report` for the operator report plus readiness audit.

Transcript matches, especially on TikTok, are navigation cues and strong
ranking evidence only. Confirmation requires direct visible sponsor-brand logo
evidence or spoken sponsor-brand promo evidence. Persistent writes require
confirmed proof plus
source video URL, screenshot id/share URL, view count, and
`observedPromoAtSeconds`.

Final `format_verification_report`, `workflow_report`, and
`audit_workflow_readiness` responses include top-level `verificationRows` and
`verificationLinks` when candidate rows are supplied; use those fields for the
operator handoff so each source video URL stays beside its screenshot id/link
and write-gate state without parsing Markdown report text.
`proof_run_harness` is the final no-write classifier before an operator acts on
a run. It accepts candidate/search rows, current proof screenshots, and prior
view-log/import rows as `existingEntries`; matching screenshot IDs, screenshot
URLs, source/video URLs, or video IDs block duplicates. Profile/grid screenshots
remain review evidence unless their `sourceUrl` matches the canonical source
video URL.

Before broad `/grillme` automation, use
`pnpm agent:native-automation:check --json`,
`pnpm agent:smoke:primitives --mock --json`, or the default
`pnpm agent:readiness:check --json` gate to prove the native/local automation
catalog and core primitive tools. The native catalog is static and no-write; it
emits `nativeAutomationFormat: dealdash-native-automation-catalog-v1`, verifies
DJA-130 native/local replacements and Apify escalation policy, and must keep
`nativePathCoverageStatus: complete`, `apifyPolicyStatus: documented`,
`liveWrites: false`, and `persistentViewLogWrites: false`. The primitive JSON
contract must expose `contextSearch`, `dueViews`, `viewLogs`, `urlShortLink`,
`imageUploadShortLink`, `screenshotCapture`, and a three-row
`verificationLinks` set so agents can see the source URL, returned short link,
uploaded image short link, and specified screenshot capture proof separately.

Use the mock workflow smoke before claiming the flow is ready:

```bash theme={null}
pnpm agent:smoke:linkshot-workflow --mock --json
```

The smoke verifies the full MCP chain with a transcript-only row and a confirmed
direct-proof row, but it does not perform live platform search or persistent
view-log writes.

Use the mock browser collector smoke before claiming browser-visible platform
pages can feed the evidence workflow:

```bash theme={null}
pnpm agent:smoke:linkshot-browser --mock --json
```

This opens local platform-like pages with Playwright, extracts candidate fields
and transcript/caption cues, captures views-card proof with
`screenshots.capture_url`, and feeds the same `proofScreenshots` contract used
by live collectors.

For a no-write live pass against explicit platform URLs, run:

```bash theme={null}
pnpm agent:preflight:linkshot-live \
  --url https://www.tiktok.com/@creator/video/123 \
  --requested-date 2026-04-10 \
  --promo-code TODAYCODE \
  --platform tiktok \
  --expected-account-url https://www.tiktok.com/@creator \
  --json
```

Use `pnpm agent:preflight:linkshot-live --mock --json` for validation. Add
`--from-due-views` to validate read-only due-view URL seeding when no explicit
`--url` is available. The preflight opens only supplied HTTP(S) URLs or
candidate URLs resolved from `deals.due_views`, blocks local/private targets by
default, extracts candidate fields, and runs the LinkShot plan, intake,
transcript-cue, ranking, proof-capture handoff, and direct-proof review helpers.
It keeps
`liveWrites: false` and `proofCapture: false`; it does not search platforms,
capture/upload proof images, create short links, or write view logs. Use
`proofCapturePlan.jobs` as the next screenshot handoff.

When an operator forwards a Telegram batch of social links, save the raw message
body to a UTF-8 text file and run
`pnpm agent:preflight:linkshot-live --from-telegram-links path/to/telegram-links.txt --requested-date YYYY-MM-DD --json`.
The preflight reports `forwardedLinkImport`, canonicalizes
Instagram/TikTok/YouTube/Facebook links, collapses duplicate URL variants, keeps
profile/grid URLs as no-write review evidence, and blocks missing requested
dates instead of guessing them. Those rows use the same no-write intake/ranking
path as external candidate JSON. When Telegram rows are mixed with external
candidate JSON/JSONL rows, intake blockers stay failing unless every candidate in
the run came from `telegram_forwarded_links`.

Use
`pnpm agent:preflight:linkshot-live --mock --from-plan-targets --discover-candidates --json`
to validate planned account-page discovery. With `--from-plan-targets`, the
preflight opens URL-bearing `account_page` targets returned by
`plan_video_search.browserSearchTargets`, reports `planTargetSeed`, and does not
open broad web/platform search targets or write anything.
Before using this mode for a `/grillme` run, read
`plan_video_search.requestReadiness`
(`linkshot-grillme-request-readiness-v1`). It must show browser search or
account discovery readiness, or else use its missing fields and operator
question instead of guessing the date, promo/query anchor, or account URL.
Use `pnpm agent:preflight:linkshot-live --mock --mock-structured-metadata --json`
to validate structured video metadata fallback extraction without screenshots,
short links, or view-log writes.

Use `pnpm agent:preflight:linkshot-live --mock --discover-candidates --json` to
validate account/search-page discovery. With `--discover-candidates`, supplied
URLs are treated as explicit pages to inspect for linked video candidate cards;
rows are marked `collectorSource: explicit_page_discovery`, deduped by video
URL, and passed through the same read-only gates. It does not run platform
search beyond those supplied pages.

Use
`pnpm agent:preflight:linkshot-live --mock --discover-candidates --enrich-discovered-candidates --json`
to validate capped detail-page enrichment. With
`--enrich-discovered-candidates`, discovered candidate detail URLs are opened up
to `--enrich-limit` (default 5) to fill sparse caption, first-comment,
transcript, view-count, and direct-proof fields. It is still bounded to the
supplied-page results and does not capture screenshots, create short links, or
write view logs.
Detail-page extraction reads visible DOM first and then falls back to JSON-LD or
known app-state/player JSON scripts for source URL, account, title,
caption/description, transcript, published date, video id, and view count. When
visible/structured transcript text is missing, it may read standard
caption/subtitle track files from DOM `<track>` tags or app-state/player JSON
such as YouTube timed-text `captionTracks`, and mark candidates with
`transcriptSource: caption_track`, track URL, kind, label, language, and track
source. Structured and caption-track transcript/caption cues remain
navigation-only and must not be copied into direct-proof fields.

Use `pnpm agent:preflight:linkshot-live --mock --capture-proof --json` to
validate the explicit screenshot step. With `--capture-proof`, the command
consumes the handoff jobs, calls `screenshots.capture_url`, and passes returned
screenshot metadata back into `workflow_report` as `proofScreenshots`. It can
create screenshot proof records and short screenshot links, but it still keeps
`persistentViewLogWrites: false` and does not write LinkShot view logs.
Imported or extracted `screenshotSelector`, `waitForSelector`, and
`captureClip` hints are preserved on the matching capture job as
`candidateCaptureHint`, while an explicit CLI `--selector` remains the operator
override. If the screenshot API response omits `sourceUrl`, the preflight
preserves the source video URL from the capture job so operator rows still keep
the video link beside the screenshot link.

Use `pnpm agent:preflight:linkshot-live --mock --mock-caption-track --json` to
validate caption/subtitle track cue extraction without screenshots, short links,
or persistent view-log writes.
Use
`pnpm agent:preflight:linkshot-live --mock --mock-app-state-caption-track --json`
to validate app-state/player JSON caption-track extraction without screenshots,
short links, or persistent view-log writes.
The default `pnpm agent:readiness:check --json` gate runs both focused
browserless caption-track preflights; use
`pnpm agent:readiness:check --only preflight_caption_track,preflight_app_state_caption_track --json`
when validating transcript cue handling alone. These checks import
deterministic caption-track candidate fixtures instead of launching Playwright,
must keep `persistentViewLogWrites` and `proofCapture` false, and summarize
transcript cue counts without treating the cue as direct proof. The raw
`--mock-caption-track` and `--mock-app-state-caption-track` commands remain the
browser-backed extraction checks for runners with Playwright browsers installed.

Use
`pnpm agent:readiness:check --only proof_capture_rehearsal --json`
to validate the CI-stable browserless no-view-log-write rehearsal: imported
candidate rows, one proof screenshot, `workflow_report`, and
`directProofReviewPlan`.
Use
`pnpm agent:preflight:linkshot-live --mock --discover-candidates --enrich-discovered-candidates --capture-proof --capture-limit 1 --json`
only when Playwright browsers are installed and you also need to exercise
explicit page discovery plus capped detail enrichment before capture. The
report must keep screenshot links beside source video URLs and keep
transcript-only or missing-screenshot rows blocked. The
preflight also exposes top-level `verificationRowsFormat:
linkshot-live-preflight-verification-row-v1` and `verificationRows`; use those
rows for operator output because each row keeps source video and screenshot
verification links together with direct-proof review status, suggested seek
seconds, review window seconds, and no-write reason.

Use `pnpm agent:inventory:linkshot-screenshots --mock --json` to validate the
historical screenshot example inventory. Live mode is read-only and calls
`screenshots.list_latest` before `linkshot_evidence.summarize_screenshot_examples`.
The command outputs screenshot IDs, short links, source video URLs,
platform/deal/promo metadata, detected views, capture patterns,
`missingForTraining`, and next actions. It omits raw image payloads, OCR text,
storage paths, owner names, and public image URLs by default. Add
`--include-view-logs` when the inventory needs dated LinkShot view-log rows,
response-level `viewLogCopyText`, the extension-compatible `copyTextFormat`, and
`verificationRows` that keep `checkedAt`, `logId`, source video URL, screenshot
short link, views, and copy text together. `--from`, `--to`, `--q`, and
`--log-id` narrow the view-log read. Rows missing source or screenshot links are
marked `needs_metadata` for curation. Treat the output as capture-framing
examples only; current video proof must still be captured fresh and supplied as
`proofScreenshots`.
The default readiness gate also runs
`pnpm agent:inventory:linkshot-screenshots --mock --include-view-logs --date 2026-04-10 --json`
so dated example selection, `verificationRows`, and
`linkshot-extension-completed-copy-v1` copy text stay covered by the main
`/grillme` health check.

## Symphony Pilot Guardrail

DealDash has a repo-owned `/WORKFLOW.md` for openai/symphony-style Linear/Codex
orchestration. Treat it as the dispatch contract for pilot runs. It only allows
safe `Todo` issues in the `Symphony: LinkShot Evidence Agent` project with
label `symphony-pilot`, max concurrency `1`, no deletes, no payments, no
external messages, and no persistent LinkShot view-log writes.

Validate changes with:

```bash theme={null}
pnpm agent:workflow:check
```

## Template Reads

Use `templates.latest_story_message` when the user asks for the latest story copy, story message, or "latest story from templates". Use `templates.list_message` with `tag=story` if the latest endpoint returns empty. Use `templates.list_deal` or `templates.latest_deal` for reusable deal/package templates.

Template reads are Tier 0 and remain scoped to the acting user.

## LinkShot View Logs

`deals.view_logs` is a Tier 0 read operation routed through
`/api/agent/deals/view-logs`. It mirrors the website's `/screenshots` View Logs
tab and supports `scope`, `dealId`, `videoId`, `logId`, `from`, `to`, `q`,
`limit`, and `offset` filters.

Responses include response-level `copyText` for the returned page/filter and
per-row `copyText` for exact log verification. Both use
`copyTextFormat: "linkshot-extension-completed-copy-v1"`, matching the LinkShot
extension completed-copy action and the website View Logs copy buttons.
Website deep links such as
`/screenshots?tab=logs&logId=view_log_id&from=2026-04-10&to=2026-04-10`
open and select one exact row for copying; date-only `from`/`to` filters cover
full checked days. If the operator clears the focused selection, refreshes do
not reselect it unless the URL focuses a different `logId`.

The bridge applies the same acting-user boundaries as the DealDash website:
owned records are visible by user ID, team-shared deal logs require active team
membership and deal sharing, and screenshot previews are included only when the
acting user owns or can explicitly view the screenshot.

## Bulk Add LinkShot View Checks

Use `deals.bulk_add_view_checks` when the user sends a bulk list of post links
and a post date. It routes to `POST /api/agent/deals/bulk-import/process`.
This is a logged Tier 1 operational write, so agents should run it directly
when asked to add video/post links for LinkShot view checking.

Input goes in `params`:

* `postDate`: required, for example `2026-04-13`
* `linksText`: pasted links; blank lines separate people/groups
* `collections`: structured alternative, each item has `urls`, optional `promoCode`, optional `name`, and optional `dealDetails` (`name`, `price`, `viewsTarget`, `promoLink`, `contact`, `notes`)

The response includes:

* `created`: videos added to matched owned deals or newly created owned deals
* `needsPromoCode`: grouped links that need a promo code before a deal/video can be created
* `errors`: groups that failed validation or insertion

Never invent promo codes. If `needsPromoCode` is not empty, reply with those
links and ask for the missing promo code plus any available deal details. Each
item includes `nextAction.agentPrompt`, `requiredFields`, `optionalFields`, and
`retryParams`. Use that as the pipeline: collect promo/deal details, retry
`bulk_add_view_checks` with the same `postDate` and grouped `urls`, and DealDash
will create or reuse the owned deal and add the video to LinkShot view checking.
Team-shared deal matches remain read-only and are returned for promo-code
review. Direct standalone deal/payment writes and deletes still follow the
sensitive-write approval policy. If this endpoint returns `approval_required`,
treat it as a stale backend/policy deployment mismatch for this flow instead of
switching to generic deal create/update.

## Context Search And Memory

Use `context.search` before guessing exact IDs. It searches deals, screenshots, LinkShot logs, short links, contacts, influencers, and active memory. Useful filters:

* `q`: text query
* `scope`: `mine` or `mine-and-shared`
* `types`: comma-separated list such as `deals,screenshots,view_logs,memory`
* `limit` and `offset`

Search results include a `resourceUri`, web link when available, access/ownership context, and metadata. Shared data still follows website sharing rules.

Use `memory.create` only after telling the user the exact title, content, tags, and source you will save. Memory is visible, account-scoped, and searchable through `context.search`.

Use `memory.archive` to remove stale memory from active context. Do not promise permanent deletion in V1.

## Related Settings Page

See [AI Agent Connection Settings](dealdash-agent-connection-settings.mdx) for the in-app BYOA endpoint, Settings UI, security rules, and test map.
