Skip to content
Merged
Show file tree
Hide file tree
Changes from 18 commits
Commits
Show all changes
26 commits
Select commit Hold shift + click to select a range
93cd0d2
CON-112: Campaign Assistant — per-campaign chat over content_plan + e…
grsmv Jul 14, 2026
cf30301
CON-112: tests + http-client for Campaign Assistant
grsmv Jul 14, 2026
4b0fa18
CON-112: make history write non-fatal + atomic
grsmv Jul 14, 2026
7c1510a
CON-112: ListMessages returns [] not null for empty history
grsmv Jul 14, 2026
b0419b5
CON-112: PostsHandler.ListMessages returns [] not null for empty history
grsmv Jul 14, 2026
f237fcf
CON-113: campaign overview — brief, phases, content distribution
grsmv Jul 14, 2026
0d45644
CON-113: move campaignoverview to campaign_actions/overview
grsmv Jul 14, 2026
a7eb6e0
refactor: extract duplicated JSONStringScanner to genkit/jsonstream
grsmv Jul 14, 2026
7d39dcf
CON-114: targeted content generation (add posts by platform/phase/tim…
grsmv Jul 14, 2026
5c51ed2
CON-114: refresh Campaign Assistant system prompt for current capabil…
grsmv Jul 14, 2026
09659dd
CON-115: change campaign dates & redistribute non-published content
grsmv Jul 15, 2026
0649b93
CON-114: preserve resolveAssets warnings in targeted generation
grsmv Jul 15, 2026
891b2ab
jsonstream: emit carried partial-UTF8 tail at end of a watched string
grsmv Jul 15, 2026
3497ad6
CON-116: brief & content consistency review
grsmv Jul 15, 2026
60945f5
Remove prototyping — extracted to ../ui-prototyping with full history
grsmv Jul 15, 2026
9fcc984
CON-112: instrument assistant turn + slim router + parallelize conten…
grsmv Jul 15, 2026
8389fcc
CON-112: keep the useful perf diagnostics, drop the dead-end workarounds
grsmv Jul 16, 2026
2788156
CON-112: stabilize Anthropic tool order to fix ~50s per-request latency
grsmv Jul 16, 2026
7cad1b5
CON-112: extend tool-cache pre-warm to post_assistant
grsmv Jul 16, 2026
101536c
CON-112: document tool-order/strict-schema latency in add-genkit-flow…
grsmv Jul 16, 2026
bc6c47d
CON-112: fix setCampaignDates error handling and count ordering
grsmv Jul 16, 2026
39346dd
CON-112: cap posts-review limit at configured maximum
grsmv Jul 16, 2026
34c26d8
CON-112: guard batch reschedule against ineligible posts
grsmv Jul 16, 2026
fe0bf89
CON-112: close resp.Body in anthropic logging transport test
grsmv Jul 16, 2026
85698a3
CON-112: don't mutate caller's request in tool-order transport
grsmv Jul 16, 2026
5667017
CON-117: fill in a missing generatePosts date-window bound
grsmv Jul 17, 2026
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
204 changes: 204 additions & 0 deletions http-client/campaigns/campaigns.http
Original file line number Diff line number Diff line change
Expand Up @@ -186,3 +186,207 @@ Content-Type: application/json
### Enrich campaign brief — non-existent ID (expects 404) [protected]
POST {{baseUrl}}/api/campaigns/nonexistent/enrich-brief
Accept: text/event-stream

###
# ── Campaign Assistant — CON-112 ──────────────────────────────────────────────
###
# A conversational, per-campaign assistant. Available to ANY user in the
# campaign's tenant (no owner-only guard). It routes natural-language
# instructions to the content_plan and enrich_brief flows (run as tools) or
# answers grounded questions about the campaign.
#
# A cheap planning model (PLANNING_MODEL_ID, Haiku) drives routing; the wrapped
# flows do the heavy prose on the generation model. enrichBrief auto-applies the
# new brief to the campaign; runContentPlan persists the draft posts.
#
# Requires ANTHROPIC_API_KEY on the server; returns 503 when the flow is unwired.
# Returns 400 when "instruction" is missing/empty.
# Response is a Server-Sent Events stream. Events:
# "explanation_delta" ({"delta":"..."}) — the conversational reply streaming;
# "tool_call" / "tool_result" — the assistant invoking a tool;
# "content_plan_started" / "content_plan_step" / "content_plan_post" /
# "content_plan_warning" / "content_plan_complete" — forwarded, namespaced,
# while runContentPlan runs;
# "enrich_brief_started" / "enrich_brief_description_delta" /
# "enrich_brief_persona_delta" / "enrich_brief_messages_delta" /
# "enrich_brief_tone_delta" / "enrich_brief_complete" — forwarded while
# enrichBrief runs;
# "complete" (the full CampaignAssistantResponse — the source of truth);
# "error" ({"message":"...","code":<http_code>}).

### Campaign assistant — generate a content plan [protected]
POST {{baseUrl}}/api/campaigns/{{campaignId}}/assistant
Accept: text/event-stream
Content-Type: application/json

{
"instruction": "Generate a content plan for this campaign."
}

### Campaign assistant — enrich / improve the brief [protected]
POST {{baseUrl}}/api/campaigns/{{campaignId}}/assistant
Accept: text/event-stream
Content-Type: application/json

{
"instruction": "Improve the campaign brief — make the tone more B2B, technical, and benchmark-driven."
}

### Campaign assistant — change campaign dates [protected] (CON-115)
# action=dates_updated; if drafts now fall outside the new range, the reply
# offers to redistribute (it won't unless you ask). Chat-only — no REST endpoint.
POST {{baseUrl}}/api/campaigns/{{campaignId}}/assistant
Accept: text/event-stream
Content-Type: application/json

{
"instruction": "Move the campaign end to the beginning of July."
}

### Campaign assistant — redistribute non-published posts [protected] (CON-115)
# action=posts_redistributed; re-dates only draft + ready-for-publish posts
# evenly across the timeline (phase by phase). Never moves scheduled/published.
POST {{baseUrl}}/api/campaigns/{{campaignId}}/assistant
Accept: text/event-stream
Content-Type: application/json

{
"instruction": "Redistribute the drafts and unpublished posts across the campaign."
}

### Campaign assistant — review the brief for consistency [protected] (CON-116)
# action=brief_reviewed; read-only. Returns findings (aspect/severity/issue/
# suggestion) and, when there are issues, offers to improve the brief with
# enrichBrief (it won't unless you ask). Response carries "briefReview".
POST {{baseUrl}}/api/campaigns/{{campaignId}}/assistant
Accept: text/event-stream
Content-Type: application/json

{
"instruction": "Is the brief consistent? Review it and suggest improvements."
}

### Campaign assistant — check posts against the brief [protected] (CON-116)
# action=posts_reviewed; read-only. Checks the campaign's non-published posts
# (capped) against the brief and returns per-post drift findings. Response
# carries "postsReview" (checked / total / capped / findings).
POST {{baseUrl}}/api/campaigns/{{campaignId}}/assistant
Accept: text/event-stream
Content-Type: application/json

{
"instruction": "Do the posts follow the brief? Flag any that drift."
}

### Campaign assistant — grounded question (no mutation) [protected]
POST {{baseUrl}}/api/campaigns/{{campaignId}}/assistant
Accept: text/event-stream
Content-Type: application/json

{
"instruction": "Summarise the brief and tell me what posts already exist in this campaign."
}

### Campaign assistant — out-of-scope request (expects action=declined) [protected]
POST {{baseUrl}}/api/campaigns/{{campaignId}}/assistant
Accept: text/event-stream
Content-Type: application/json

{
"instruction": "Delete all of my posts and cancel my subscription."
}

### Campaign assistant — missing instruction (expects 400) [protected]
POST {{baseUrl}}/api/campaigns/{{campaignId}}/assistant
Accept: text/event-stream
Content-Type: application/json

{}

### Campaign assistant — non-existent campaign (streams error, campaign not found) [protected]
POST {{baseUrl}}/api/campaigns/nonexistent/assistant
Accept: text/event-stream
Content-Type: application/json

{
"instruction": "Generate a content plan."
}

### List campaign assistant messages [protected]
# Returns up to the 50 most-recent conversation turns (oldest-first).
GET {{baseUrl}}/api/campaigns/{{campaignId}}/messages
Accept: application/json

### Campaign overview — brief, phases, content distribution [protected] (CON-113)
# Synchronous JSON (not SSE). Returns the brief recap, the campaign's phases with
# per-phase post counts, and content distribution by status, platform, and
# content type. Available to any user in the campaign's tenant.
# 404 if the campaign does not exist (or belongs to another tenant).
GET {{baseUrl}}/api/campaigns/{{campaignId}}/overview
Accept: application/json

### Generate targeted posts — add a few posts for a platform/phase/window [protected] (CON-114)
# Adds NEW draft posts for a platform subset, a single phase, and a publish-date
# window (deterministic params — the assistant tool resolves natural language,
# this endpoint takes concrete ids/dates). Count is clamped to GENERATE_POSTS_MAX.
# Requires ANTHROPIC_API_KEY; 503 when unwired. 400 on bad params (unknown/foreign
# platform, unknown phase, count < 1, bad window); 404 if the campaign is missing.
# Response is an SSE stream: step / post / warning / complete (ContentPlanResponse) / error.
POST {{baseUrl}}/api/campaigns/{{campaignId}}/generate-posts
Accept: text/event-stream
Content-Type: application/json

{
"platformIds": ["AXqWG7U2qnpt"],
"phaseId": "<phase-id-from-the-campaign-type>",
"count": 3,
"windowStart": "2026-07-20",
"windowEnd": "2026-08-03",
"postType": "text-post"
}

###
# The natural-language equivalent goes through the Campaign Assistant:
# POST /api/campaigns/{{campaignId}}/assistant
# { "instruction": "Add a few Threads posts in the current phase for the upcoming weeks" }
###

###
# ── Consistency reviews — CON-116 ─────────────────────────────────────────────
###
# Read-only reviews. The brief review checks the brief's internal consistency and
# completeness; the posts review checks whether the campaign's non-published posts
# follow the brief. Neither mutates anything — to apply brief fixes, call
# enrich-brief (or ask the assistant to enrich the brief).
# Requires ANTHROPIC_API_KEY; 503 when unwired. 404 if the campaign is missing.
# Response is an SSE stream: step / complete / error.
# brief review complete → BriefReview { consistent, findings[], summary }
# posts review complete → PostsReview { checked, total, capped, aligned, findings[], summary }

### Review the campaign brief for consistency [protected] (CON-116)
# No body. Read-only — does NOT change the brief.
POST {{baseUrl}}/api/campaigns/{{campaignId}}/brief-review
Accept: text/event-stream

### Review the brief — non-existent campaign (expects 404) [protected]
POST {{baseUrl}}/api/campaigns/nonexistent/brief-review
Accept: text/event-stream

### Check campaign posts against the brief [protected] (CON-116)
# Body is optional — an empty POST uses the default cap (CONSISTENCY_POSTS_MAX).
# Send {"max": N} to cap how many non-published posts are checked.
POST {{baseUrl}}/api/campaigns/{{campaignId}}/posts-review
Accept: text/event-stream
Content-Type: application/json

{
"max": 10
}

### Check campaign posts — default cap, empty body [protected] (CON-116)
POST {{baseUrl}}/api/campaigns/{{campaignId}}/posts-review
Accept: text/event-stream

### Check campaign posts — non-existent campaign (expects 404) [protected]
POST {{baseUrl}}/api/campaigns/nonexistent/posts-review
Accept: text/event-stream
12 changes: 0 additions & 12 deletions prototyping/CON-28-campaign-content-plan-generation/index.html

This file was deleted.

Loading
Loading