Skip to content

Update 2.6.x with new changes#758

Merged
BaiyuScope3 merged 80 commits into
2.6.xfrom
main
Jan 13, 2026
Merged

Update 2.6.x with new changes#758
BaiyuScope3 merged 80 commits into
2.6.xfrom
main

Conversation

@BaiyuScope3

Copy link
Copy Markdown
Collaborator

No description provided.

BaiyuScope3 and others added 30 commits January 7, 2026 14:40
Allow additional properties in all JSON schemas for forward compatibility.
This enables clients to ignore unknown fields when parsing responses,
making it easier to evolve the API without breaking existing integrations.

Generated dist/schemas/2.5.2/ with relaxed validation.
chore: sync v2.6-rc docs and schemas from 2.6.x branch
… Slack channels (#675)

- Add isValidCommitteeSlug() function that allows slashes in slugs for
  industry_gathering committee types (e.g., industry-gatherings/2026/ces)
- Prevent confusing patterns like consecutive slashes/hyphens
- Auto-create Slack channel when creating industry gatherings without one
- Include warning in response if channel auto-creation fails
- Update HTML pattern to allow slashes in slug input
- Add industry gathering tools to Addie's system prompt

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-authored-by: Claude Opus 4.5 <noreply@anthropic.com>
* fix: update dependencies to address security vulnerabilities

Updates @adcp/client, @modelcontextprotocol/sdk, @workos-inc/node, and qs
to latest versions with security patches.

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>

* chore: add empty changeset

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>

---------

Co-authored-by: Claude Opus 4.5 <noreply@anthropic.com>
…onstraint_relax_schema

feat: add additionalProperties: true to all schemas (v2.5.2)
* feat: add outreach response tracking and goal follow-up system

- Track DM responses to proactive outreach messages
- Track link click conversions when users sign up via outreach links
- Add goal follow-up job that sends gentle reminders for unanswered outreach
- Add goal outcome reconciliation (check if goals were achieved through other means)
- Add admin API endpoints for follow-up preview, reconciliation preview, and user timeline view
- Add follow_up_template, max_attempts, days_between_attempts to outreach_goals schema

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>

* fix: rename migration to 149 to avoid duplicate version

Migration 148 was added to main (148_slack_only_engagement_scores.sql)
while this branch was in progress.

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>

* fix: rename migration to 150 to avoid duplicate version

Migration 149 was added to main (149_simplify_engagement_scoring.sql)
while this branch was in progress.

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>

---------

Co-authored-by: Claude Opus 4.5 <noreply@anthropic.com>
- Fix RSS feed guid.split error by handling object-style guids from RSS parser
- Remove non-existent 'role' column from organization_memberships INSERT
- Add defensive string coercion and warning log in generateSlug

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-authored-by: Claude Opus 4.5 <noreply@anthropic.com>
…669)

* fix: resolve Slack/WorkOS user ID duplicates in chapter memberships

Root cause: Users who joined chapters via Slack had their Slack user ID stored.
When they later linked their WorkOS account via web login, they got a different ID.
The slack_user_mappings table linked these, but membership/leader records still
had the old Slack IDs, causing duplicates and broken permissions.

Solution:
- Add resolveToCanonicalUserId() helper to resolve Slack IDs to WorkOS IDs
- Apply resolution in all write paths: addMembership, addLeader, setLeaders,
  addMembershipWithInterest
- Apply resolution in all read/check paths: isMember, isLeader, getMembership,
  removeMembership, removeLeader, deleteMembership, getCommitteesLedByUser
- Add migration 149 to consolidate existing duplicate records
- Add canonical_user_id to WorkingGroupLeader type for frontend compatibility
- Update frontend and backend leader checks to use canonical_user_id

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>

* fix: use joined_at for membership deduplication and empty changeset

- Migration 149: Use joined_at instead of UUID id for deduplication
  (UUIDs are not sequential, so id comparison doesn't determine age)
- Empty changeset since this doesn't impact the protocol

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>

* fix: rename migration 149 to 151 to avoid conflict with main

Migration 149 and 150 already exist on main branch.

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>

---------

Co-authored-by: Claude Opus 4.5 <noreply@anthropic.com>
- Fix Stripe API: Use `discounts` array instead of deprecated `coupon` parameter
  for subscription discounts (the previous syntax was silently ignored)
- Add precision model routing: billing/financial queries now use Opus for accuracy
- Invoice discounts: admins can now apply discounts when sending invoices
- Admin tools: switch from tier names to lookup_key for product selection
- Add coupon_id support to send_invoice billing tool

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-authored-by: Claude Opus 4.5 <noreply@anthropic.com>
* feat: unified content management with authorship separation (#670)

Implements unified content management system with separation of authorship
from ownership, supporting co-authored content and proposal workflows:

**Database Migration (150):**
- Create content_authors table for co-authoring support
- Add proposer tracking fields to perspectives table
- Add review tracking (reviewed_by_user_id, reviewed_at, rejection_reason)
- Update status constraint to include pending_review and rejected
- Create helper functions: is_committee_lead(), is_content_owner()

**Content Routes (/api/content):**
- POST /propose - Submit content to personal or committee collections
- GET /pending - List pending content for review (leads/admins)
- POST /:id/approve - Approve pending content
- POST /:id/reject - Reject with reason

**My Content Routes (/api/me/content):**
- GET / - Unified view of user's content (author/proposer/owner)
- PUT /:id - Update content user owns
- POST /:id/authors - Add co-author
- DELETE /:id/authors/:authorId - Remove co-author

**Addie Tools:**
- propose_content - Create content targeting personal or committee
- get_my_content - View all user's content with relationships
- list_pending_content - Review queue for leads/admins
- approve_content - Approve and publish pending content
- reject_content - Reject with feedback

**Proactive Notifications:**
- Added pending_content to MemberContext interface
- Committee leads and admins see pending counts in context
- Addie proactively mentions pending items needing review

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>

* feat: add unified CMS page at /my-content

- Create my-content.html with unified content management UI
  - Shows user's content (authored, proposed, owned)
  - Pending review tab for committee leads and admins
  - Create/edit content with committee targeting
  - Approve/reject workflow for reviewers
  - Filter by status and collection

- Update /admin/perspectives to redirect to /my-content
- Update admin APIs to accept pending_review and rejected statuses

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>

* fix: collections API and my-content improvements

- Fix SQL queries to use correct column names (workos_user_id)
- Add GET /api/content/collections endpoint for public and user committees
- Add migration 151 for public content collections (Editorial, Training)
- Update my-content.html status hint logic to use collection data
- Remove deprecated admin-perspectives.html

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>

---------

Co-authored-by: Claude Opus 4.5 <noreply@anthropic.com>
* feat: add Slack history backfill for Addie's search_slack tool

Adds the ability to backfill historical Slack messages so Addie can
search community discussions beyond the 5-day window of real-time
indexing.

Features:
- Fetches messages from public channels (private opt-in)
- Includes thread replies with rate limiting
- Excludes sensitive channels (admin, billing) by default
- Idempotent via upsert (safe to run multiple times)

New API endpoints:
- POST /api/admin/addie/backfill/slack - trigger backfill
- GET /api/admin/addie/backfill/slack/status - check index stats

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>

* feat: add channel filter to search_slack tool

Enables Addie to filter Slack search results by channel name, which is
needed for requests like "summarize Governance working group discussions".

Changes:
- Added channel parameter to search_slack tool schema
- Updated tool description with guidance on channel filtering
- Database query now supports optional channel filtering with partial match
- Increased default/max limits to support summary use cases (10/25)

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>

* feat: add get_channel_activity tool for channel summaries

Adds a dedicated tool for getting recent activity from a Slack channel
without requiring keyword search. This is the right tool for requests like
"summarize the Governance working group discussions".

Changes:
- Added get_channel_activity tool with channel, days, and limit params
- Added getChannelActivity database method that fetches by recency
- Response includes message count, most active users, and formatted messages
- Provides guidance for synthesizing results into summaries

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>

---------

Co-authored-by: Claude Opus 4.5 <noreply@anthropic.com>
The migration was failing because updating Slack IDs to WorkOS IDs
would violate the unique constraint when both records already exist.

Fix: Delete duplicate Slack-based records first (where a WorkOS-based
record already exists), then safely update the remaining records.

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-authored-by: Claude Opus 4.5 <noreply@anthropic.com>
* fix: resolve Slack/WorkOS leader IDs at read time for permission checks

Chapter leaders added via Slack ID before linking their WorkOS account
couldn't manage events because the leader permission checks compared
stored Slack IDs against login WorkOS IDs.

Changes:
- getLeaders/getLeadersBatch: Resolve canonical_user_id via slack_user_mappings
- isLeader: Check both direct user_id match and Slack→WorkOS mapping
- getCommitteesLedByUser: Support both ID types with DISTINCT count
- content.ts: Fix is_leader subqueries to use mapping join
- member-context.ts: Fix pending content leader query
- outbound-db.ts: Fix capability counts for leaders

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>

* refactor: remove redundant resolveToCanonicalUserId calls

Based on code review feedback:
- Remove redundant resolveToCanonicalUserId() calls in isLeader() and
  getCommitteesLedByUser() since the JOIN handles ID resolution
- Add consistent AND sm.workos_user_id IS NOT NULL to all JOINs

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>

---------

Co-authored-by: Claude Opus 4.5 <noreply@anthropic.com>
…#689)

Two issues were causing DM responses to appear incorrectly:

1. For Slack AI/Assistant apps, every DM is treated as a thread. The code
   was not passing thread_ts when responding, causing responses to appear
   as separate notifications/threads instead of in the same conversation.

2. The Assistant framework receives message.im events through a separate
   pipeline from global middleware, causing both handleDirectMessage and
   handleUserMessage to fire for the same event, resulting in duplicate
   responses.

Changes:
- Add thread_ts: event.ts to chat.postMessage in handleDirectMessage
- Add guard in handleUserMessage to skip DMs without thread_ts (handled
  by handleDirectMessage via middleware)

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-authored-by: Claude Opus 4.5 <noreply@anthropic.com>
Some RSS feeds (e.g., Marketing Week) return guid elements with only
XML attributes but no text content, resulting in objects like
`{"$":{"isPermaLink":"false"}}` instead of strings. This caused
"Cannot convert object to primitive value" errors.

- Update FeedItem type to reflect optional `_` (text) and `$` (attrs)
- Extract guid text content properly, falling back to link URL
- Remove per-feed debug logs to reduce log volume
- Only log RSS processing summary when there are new articles or errors

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-authored-by: Claude Opus 4.5 <noreply@anthropic.com>
)

- Fix "link user" button on domain health page - SQL now excludes users
  already in the target org
- Add "Link All" button to batch-link all misaligned users to their
  corporate accounts
- Fix 409 error for unverified orgs by filtering out domains already
  claimed by other organizations
- Add "Similar Organization Names" section to identify potential
  duplicate orgs for merging
- Restore "Unlinked Corporate Domains" section but filter to only show
  domains where users are in actual corporate orgs (not just personal
  workspaces)
- Add Domain Health section to account detail page showing:
  - Users with this org's domain who aren't in this org
  - Potential duplicate organizations with similar names
  - Unverified domain warnings
- Security fixes: Replace inline onclick handlers with event delegation
  and add input validation for user/org IDs

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-authored-by: Claude Opus 4.5 <noreply@anthropic.com>
- Remove incorrect x-adcp-auth header from principals-and-security.mdx
- Use standard Authorization: Bearer header (consistent with MCP spec)
- Remove implementation details from protocol security docs
- Simplify authentication.mdx to Bearer tokens only
- Add test agent config example consistent with testing.mdx

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-authored-by: Claude Opus 4.5 <noreply@anthropic.com>
When viewing "Organizations Without Verified Domains", domains that are
claimed by another organization now show which org claims them with a
clickable link. This helps admins understand why a domain can't be
automatically linked and navigate to investigate/resolve conflicts.

Changes:
- Update SQL query to return claiming org details (name, id) instead of
  just domain names
- Update UI to show claimed domains with an amber tag linking to the
  claiming org's admin page
- Add "personal" tag for personal email domains
- Use design system CSS variables for styling

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-authored-by: Claude Opus 4.5 <noreply@anthropic.com>
…th (#694)

Gmail.com and other free email providers were appearing in the "Related
Organizations" section of the domain health dashboard, incorrectly grouping
unrelated organizations together just because some of their users had Gmail
addresses.

Added filtering to the relatedDomainsResult query to exclude:
- Hardcoded free email domains (gmail.com, yahoo.com, hotmail.com, etc.)
- Admin-managed personal domains from the database

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-authored-by: Claude Opus 4.5 <noreply@anthropic.com>
- Add discount banner showing percent/amount discounts on invoice products
- Display strikethrough list prices with discounted net prices
- Show "Recommended" badge for products matching org's revenue tier
- Implement two-step invoice flow: Prepare Draft → Review → Send
- Add draft review screen showing all invoice details before sending
- Fix XSS vulnerability by replacing inline onclick handlers with data attributes and event listeners
- Sort invoice products by price (lowest first) for better UX

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-authored-by: Claude Opus 4.5 <noreply@anthropic.com>
…counts (#696)

Leaders added via Slack ID that haven't linked their WorkOS account were
showing raw Slack user IDs (e.g., U09BEKNJ3GB) instead of their names.

The getLeaders and getLeadersBatch queries now include slack_user_mappings
as an additional name source, falling back to slack_real_name or
slack_display_name before showing the raw user ID.

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-authored-by: Claude Opus 4.5 <noreply@anthropic.com>
Port all missing features from admin-org-detail.html to admin-account-detail.html:

- Payment Link modal with product selection and discount support
- Invoice modal with product selection and billing address form
- Revenue Tier selector in Edit Account modal
- Parent Organization selector in Edit Account modal
- Addie Research section (loaded asynchronously)
- Member Insights section (loaded asynchronously)
- Domain actions: verify, set primary, remove

Security improvements:
- Use event delegation with data attributes instead of inline onclick
  handlers to prevent XSS vulnerabilities
- Add 401 authentication handling to domain action functions
- Escape user-provided values in confirmation dialogs

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-authored-by: Claude Opus 4.5 <noreply@anthropic.com>
- Add resolveSlackUserDisplayName and resolveSlackUserDisplayNames
  utilities to slack/client.ts with concurrency limiting (5 at a time)
- Persist resolved users to database for future lookups
- Update member-context.ts to use shared utility
- Update addie-admin.ts thread endpoint to resolve mentions
- Update admin-addie.html to replace <@U...> mentions with @DisplayName

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-authored-by: Claude Opus 4.5 <noreply@anthropic.com>
… detection (#699)

Personal workspaces should not appear in "Related Organizations (Potential
Duplicates)" since they represent individual users, not companies that could
be merged. This change ensures:
- Personal workspaces don't show the similar orgs section when viewed
- Other organizations' duplicate detection is unchanged

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-authored-by: Claude Opus 4.5 <noreply@anthropic.com>
* feat: add Google Docs integration for Addie

Allows Addie to read Google Docs, Sheets, and Drive files shared with her
Google account. When access is denied, Addie asks the user to share the
document with addie@agenticadvertising.org.

Key changes:
- New google-docs.ts module with OAuth2 token management
- read_google_doc tool for Addie to fetch document content
- Content curator now indexes Google Docs shared in public channels
- OAuth setup script for initial credential configuration

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>

* fix: use dotenv override to ensure .env.local values take precedence

The test script was failing because existing GOOGLE_CLIENT_ID and
GOOGLE_CLIENT_SECRET values in the shell environment were not being
overwritten by dotenv. Using override: true and dynamic import ensures
the correct credentials from .env.local are used.

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>

* chore: add empty changeset for Google Docs integration

This change adds Google Docs reading capability to Addie without
affecting the protocol schemas.

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>

* fix: address code review feedback for Google Docs integration

- Log docId instead of full URL to avoid exposing sensitive document info
- Add document ID validation with regex pattern for better security
- Export error prefix constants for reliable error detection in content-curator
- Use exported constants instead of hardcoded strings for error checking

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>

---------

Co-authored-by: Claude Opus 4.5 <noreply@anthropic.com>
Personal workspaces represent individual users and should never be
merged with company organizations. This adds defense-in-depth:

- Block the merge in mergeOrganizations() with a clear error message
- Show prominent warnings in previewMerge() if personal workspaces
  are incorrectly included in merge candidates
- Return 400 (not 500) for validation errors in the merge endpoint

This prevents accidental merges even if upstream duplicate detection
fails to filter personal workspaces.

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-authored-by: Claude Opus 4.5 <noreply@anthropic.com>
…ach (#702)

- Update signup emails to come from Addie with personalized content
- Different CTAs for Slack vs non-Slack users in signup emails
- Add timezone-aware business hours for proactive outreach
- Store Slack timezone offset in slack_user_mappings table
- Auto-link website and Slack accounts by email on signup
- Add OUTREACH_ENABLED kill switch for emergency disabling
- Add admin endpoints for website-only users and Slack invites

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-authored-by: Claude <noreply@anthropic.com>
… guidelines (#703)

- Remove references to non-existent aao-server repo, direct all issues to adcp
- Add CRITICAL confidentiality rules to prevent PII in public GitHub issues
- Add requirement to always include actual error messages in bug reports
- Update ecosystem docs to reflect consolidated repository structure

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-authored-by: Claude Opus 4.5 <noreply@anthropic.com>
bokelley and others added 28 commits January 12, 2026 15:47
…spects (#730)

The prospect discovery endpoints were only checking organization_domains
table, missing domains that exist only in organizations.email_domain.
This caused domains like tryclassify.com to appear as new prospects even
though they belong to existing accounts.

Changes:
- Updated slack/domains and email/domains endpoints to check BOTH
  organization_domains table AND organizations.email_domain column
- Added case normalization (lowercase) for consistent domain matching
- Added domain_sync_issues to domain-health endpoint to identify orgs
  where email_domain is set but not in organization_domains
- Added POST /api/admin/domain-health/sync endpoint to fix sync issues
- Added input validation for org_id parameter in sync endpoint

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-authored-by: Claude Opus 4.5 <noreply@anthropic.com>
…errors (#733)

* fix: add retry with exponential backoff for Anthropic API overloaded errors

The Addie bot was failing with overloaded_error (529) from Anthropic API
during high load, causing message processing to fail silently.

Changes:
- Add anthropic-retry.ts utility with isRetryableError and withRetry helpers
- Wrap processMessage API call with withRetry (3 retries, exponential backoff)
- Add inline retry loop to processMessageStream (only retries before content streams)
- Support retryable errors: overloaded_error, 500+, 429, APIConnectionError
- Add comprehensive tests for retry behavior

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>

* fix: address code review feedback for retry implementation

- withStreamRetry now throws RetriesExhaustedError for consistency
- Streaming path in claude-client wraps exhausted retries in RetriesExhaustedError
- Added tests for withStreamRetry (yields, retries, non-retryable, exhausted)
- Added test for RetriesExhaustedError.cause containing original error
- Added empty changeset

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>

---------

Co-authored-by: Claude Opus 4.5 <noreply@anthropic.com>
* feat: add admin view for council interest signups

- Add GET /api/admin/working-groups/:id/interest endpoint to fetch users who
  expressed interest in a committee
- Add "Interested Users" section in admin committee management modal
- Include "Add as Member" button to directly convert interested users to members
- Fix escapeJs function to properly handle newlines and control characters

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>

* feat: add Addie tools for council interest management

- Add DELETE /api/working-groups/:slug/interest endpoint to withdraw interest
- Add GET /api/me/working-groups/interests endpoint to list user's interest signups
- Add express_council_interest tool - opt into council notifications
- Add withdraw_council_interest tool - opt out of council notifications
- Add get_my_council_interests tool - list user's council interest signups

Users can now manage their council interest via Addie:
- "Sign me up for the retail media council"
- "I'm no longer interested in the sustainability council"
- "What councils have I signed up for?"

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>

---------

Co-authored-by: Claude Opus 4.5 <noreply@anthropic.com>
* fix: ensure numeric IDs in outreach variant API responses

PostgreSQL bigint values may be returned as strings in JavaScript.
Convert variant IDs and related numeric fields to proper JS numbers
so the frontend can correctly match variant stats with variants.

This fixes the A/B Variants tab showing no data due to type mismatch
in the comparison `s.variant_id === v.id` (string vs number).

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>

* feat: add reusable thread widget and link outreach to conversations

- Link outreach records to unified_threads when user responds to DM
- Add linkOutreachToThread() method to insights-db.ts
- Create reusable ThreadWidget (js/thread-widget.js, css/thread-widget.css)
  - Expandable inline thread viewer
  - Fetches from /api/admin/addie/threads/:id
  - Supports compact mode, timestamps, max messages
- Update admin-users.html to use thread widget:
  - Outreach history: expandable thread view per outreach
  - Recent conversations: expandable thread view per conversation
- Update admin-outreach.html history tab:
  - Add Thread column with expandable widget

This enables viewing full conversation context directly from
outreach history and user profiles without switching pages.

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>

* fix: address code review security and consistency issues

- Add XSS protection: validate/sanitize channel, threadId, containerId
- Add LRU cache eviction (max 50 entries) to prevent memory growth
- Add UUID validation for threadId in thread widget and linkOutreachToThread
- Add container ID validation (alphanumeric, hyphens, underscores)
- Add null checks in toggleExpand to prevent crashes on missing elements
- Escape thread_id in data attributes in admin-outreach.html
- Extract normalizeVariant() helper for consistent BigInt conversion
- Apply Number() conversion consistently to createVariant, getVariant, updateVariant

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>

---------

Co-authored-by: Claude Opus 4.5 <noreply@anthropic.com>
* fix: admin review/flag buttons now update modal after actions

The "Mark Reviewed", "Flag", and "Unflag" buttons in the admin thread
detail modal weren't properly updating the UI after successful API calls.

Changes:
- Add response.ok checks to properly detect HTTP errors (fetch doesn't
  throw on non-2xx responses, only on network errors)
- Refresh the thread detail modal after successful operations by calling
  viewThread() to show the updated state (reviewed badge, flag status)
- Include error messages in alert dialogs for better debugging

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>

* fix: admin review/flag buttons now update modal after actions

The "Mark Reviewed", "Flag", and "Unflag" buttons in the admin thread
detail modal weren't properly updating the UI after successful API calls.

Changes:
- Add response.ok checks to properly detect HTTP errors (fetch doesn't
  throw on non-2xx responses, only on network errors)
- Refresh the thread detail modal after successful operations by calling
  viewThread() to show the updated state (reviewed badge, flag status)
- Include error messages in alert dialogs for better debugging
- Use optional chaining and await for viewThread calls
- Apply same error handling pattern to approveItem and rejectItem

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>

---------

Co-authored-by: Claude Opus 4.5 <noreply@anthropic.com>
- Add combined user count (members + Slack-only) to accounts list API
- Add Users column to accounts table with Most Users tab view
- Combine Members and Pending Slack Users into unified Users section on detail page
- Add list_organizations_by_users Addie tool for ranking orgs
- Add list_slack_users_by_org Addie tool for viewing org Slack users
- Include Slack-only count in get_account response
- Add LIKE metacharacter escaping for SQL security
- Add member_status input validation

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-authored-by: Claude Opus 4.5 <noreply@anthropic.com>
- Fixed SENT THIS WEEK showing 0: frontend used stats.sent_week but
  backend returned sent_this_week
- Fixed RESPONSE RATE showing X/0: frontend expected stats.total_sent
  but backend didn't return it

Co-authored-by: Claude Opus 4.5 <noreply@anthropic.com>
Prevents "prompt is too long" errors when conversation history exceeds
Claude's 200K token limit. The system now:

- Estimates token counts locally using character-based heuristic
- Reserves ~50K tokens for system prompt, tools, and safety margin
- Trims oldest messages when conversation exceeds ~150K tokens
- Truncates single messages that exceed the limit
- Logs when trimming occurs for debugging

Co-authored-by: Claude Opus 4.5 <noreply@anthropic.com>
* feat: add shareable links for Addie threads in admin

- Add "Copy Link" button to copy shareable admin URL for any thread
- Add "Open in Slack" link for Slack threads to jump to original conversation
- Support ?thread= URL parameter to deep-link directly to a specific thread

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>

* fix: add input validation and error handling for thread sharing

- Validate Slack channel ID and timestamp format to prevent XSS
- Handle null return from getSlackPermalink to avoid broken links
- Add proper error handling for clipboard API
- URL encode thread ID in shareable links

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>

---------

Co-authored-by: Claude Opus 4.5 <noreply@anthropic.com>
- Implement committee leader permission check in canScheduleMeetings()
- Add group-specific leader validation in schedule_meeting handler
- Support both Slack and web channels for authorization
- Add meeting and event tools documentation to Addie's system prompt

Previously only admins could schedule meetings. Now committee leads can
schedule meetings for committees they lead, enabling self-serve meeting
management.

Co-authored-by: Claude Opus 4.5 <noreply@anthropic.com>
)

- Research section (/latest/research) now pulls from perspectives table
  where status='published' AND working_group_id IS NULL
- Other sections (industry-news, learning, etc.) continue to pull from
  addie_knowledge
- Updated Addie's propose_content responses to accurately reflect that
  perspectives will appear in /latest/research
- Added helper function and constant to reduce code duplication
- Added author_name and author_title to LatestArticle interface

Co-authored-by: Claude Opus 4.5 <noreply@anthropic.com>
Add system to tag expert content and synthesize it into knowledge rules:

- New insight_sources table for tagging emails, articles, conversations
- Synthesis job that processes sources by topic using Claude
- Side-by-side comparison UI showing current vs proposed rules
- Config versioning to track which synthesis created which rules
- Admin endpoints for managing insight sources and synthesis runs
- Seed data with foundational AdCP philosophy content

Co-authored-by: Claude Opus 4.5 <noreply@anthropic.com>
The previous fix (#740) queried perspectives where working_group_id IS NULL,
but this inadvertently included RSS and email content since those are also
stored in the perspectives table with source_type='rss' or source_type='email'.

This fix adds a filter to only show member perspectives (where source_type
is NULL or not 'rss'/'email'), ensuring the research section shows actual
thought leadership content instead of imported RSS feeds.

Co-authored-by: Claude Opus 4.5 <noreply@anthropic.com>
Meeting tools were incorrectly gated by canCreateEvents(), which only
allows aao-admin members. The canScheduleMeetings() function properly
checks for working group leadership but was never used to gate tool
availability.

This fix separates meeting tools from event tools and uses the correct
permission check, allowing committee leaders to schedule meetings for
committees they lead via Addie.

Co-authored-by: Claude Opus 4.5 <noreply@anthropic.com>
- Fix outreach_goal_stats view to use planner tables (migration 165)
- Drop insight_goals table and insight_goal_id column (migration 166)
- Update InsightsDatabase to query outreach_goals for passive extraction
- Remove admin insight goals CRUD routes, MCP tools, and UI page
- Update admin panel to show goal stats from user_goal_history

outreach_goals is now the single source of truth for both proactive
outreach and passive insight extraction during conversations.

Co-authored-by: Claude Opus 4.5 <noreply@anthropic.com>
The research section was empty because the query filtered for
working_group_id IS NULL, but all published perspectives are
assigned to the Editorial working group.

Updated the query to pull from the Editorial working group directly,
since that's where site-wide thought leadership content lives.

Co-authored-by: Claude Opus 4.5 <noreply@anthropic.com>
Migration 166 dropped the insight_goal_id column from member_outreach
but the code still referenced it, causing database errors in production
outreach scheduler.

Removed from:
- MemberOutreach interface
- CreateOutreachInput interface
- recordOutreach SQL INSERT query
- proactive-outreach.ts recordOutreach call

Goal tracking now happens via user_goal_history table instead.

Co-authored-by: Claude Opus 4.5 <noreply@anthropic.com>
The Anthropic API was rejecting requests with "prompt is too long"
(211,303 tokens > 200,000 maximum) for admin users with many tools.

Root causes:
- Static tool buffer (15K) was far below actual usage (~33K for admins)
- Conversation history trimming happened before tools were combined
- Verbose tool descriptions consuming unnecessary tokens

Changes:
- Increase tool definition buffer from 15K to 45K tokens
- Add dynamic token calculation based on actual tool count (~300/tool)
- Move tool combination before message trimming for accurate budgets
- Remove 3 deprecated tools (lookup_organization, get_organization_details,
  find_prospect) that were replaced by unified organization tools
- Trim verbose property descriptions across admin and member tools
- Add test coverage for new token estimation functions

Co-authored-by: Claude Opus 4.5 <noreply@anthropic.com>
* feat: add Addie escalation and learning capture tools

Add tools for Addie to escalate requests she cannot fulfill and capture
valuable insights from conversations:

- New `escalate_to_admin` tool for capability gaps and requests needing
  human action (creates tracked escalations, flags threads, notifies admins)
- New `capture_learning` tool to flag user insights for synthesis
- Admin API endpoints for managing escalations (list, view, update status)
- System prompt updates to prevent Addie from promising actions without tools
- Thread filtering for multi-turn only conversations in admin UI

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>

* fix: rename escalations migration to version 166

Main branch already has 165_fix_outreach_goal_stats.sql from PR #746.

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>

* fix: rename escalations migration to version 167

Main branch now has 166_drop_insight_goals.sql from PR #746.

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>

---------

Co-authored-by: Claude Opus 4.5 <noreply@anthropic.com>
…#749)

* feat: add call_adcp_agent tool and Claude Skills for full AdCP access

Enable clients to execute the full AdCP spec (not just testing API) via:
- `call_adcp_agent` tool: Low-level proxy to any AdCP-compliant agent
- Claude Skills: Protocol knowledge for media-buy, signals, and creative

Skills teach Claude the protocol schemas and workflows; the tool routes
to whatever agent the user specifies. Auth tokens are looked up from
saved agent context or can be provided directly.

Includes:
- SSRF protection via URL validation (HTTPS only, no internal networks)
- Auto-generation of skill schemas during build
- Quick reference docs for all three protocols

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>

* Address PR review comments

- Update example dates from 2024 to 2026 in media-buy-quick-reference.mdx
- Refactor build-schemas.cjs to reduce code duplication using helper function

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>

---------

Co-authored-by: Claude Opus 4.5 <noreply@anthropic.com>
Automatically share published working group posts to their linked Slack
channels. When a post is published, a formatted message is sent to the
working group's Slack channel (if configured).

- Add notifyWorkingGroupSlackChannel() for posting to specific channels
- Add notifyPublishedPost() wrapper that checks privacy and channel config
- Update committees.ts routes to use new notification function
- Add notifications to content.ts for propose/approve endpoints
- Skip members-only posts to respect privacy
- Use Block Kit for rich message formatting

Closes #719

Co-authored-by: Claude Opus 4.5 <noreply@anthropic.com>
Previously when users shared text files in Slack, Addie would only see
file metadata and wait for an explicit request to read the file. This
was frustrating UX - users expected Addie to see what they shared.

Changes:
- Update read_slack_file tool description to instruct Claude to
  proactively read files when shared, without waiting to be asked
- Add File Handling section to Addie's system prompt with explicit
  guidance on proactive file reading
- Include guidance to inform users if file content is truncated

Co-authored-by: Claude Opus 4.5 <noreply@anthropic.com>
* feat: consolidate agent testing tools into probe_adcp_agent

Replaces check_agent_health and get_agent_capabilities with a single
probe_adcp_agent tool that always validates connectivity first, then
retrieves capabilities. This addresses user confusion where health
check and capabilities returned inconsistent results.

- Removes check_agent_health tool (HTTP card validation only)
- Removes get_agent_capabilities tool (registry lookup, often cached)
- Adds probe_adcp_agent tool combining health + capabilities
- Adds error handling for health check API failures
- Adds MCP client configuration section to auth docs

Addresses Slack feedback about auth confusion and tool inconsistency.

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>

* chore: add changeset description and clarifying comment

- Add description to empty changeset for changelog history
- Clarify graceful degradation behavior in probe_adcp_agent

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>

---------

Co-authored-by: Claude Opus 4.5 <noreply@anthropic.com>
- Filter domain users endpoint to exclude pending join requests and invitations
- Add direct member addition for verified domain users (POST /api/organizations/:orgId/domain-users/add)
- Auto-join users from verified domains when they create join requests
- Add admin backfill endpoints for mass-adding domain users as members
- Add email validation and input sanitization
- Fix event listener accumulation in team.html
- Update team.html UI with Add/Invite buttons and "Add All" functionality
- Update admin domain health page with member backfill UI

Co-authored-by: Claude Opus 4.5 <noreply@anthropic.com>
… tiers (#755)

Introduces a consistent three-tier classification for organizations:
- Members: paying subscription (active, not canceled, amount > 0)
- Engaged: not paying, but has at least one user with engagement_score > 0
- Registered: not paying, no engaged users, but has at least one user on site or Slack

Key changes:
- Create shared SQL filter module (server/src/db/org-filters.ts)
- Update admin stats route to use shared filters
- Update admin accounts route to use shared filters
- Update admin prospects route to use shared filters
- Update admin-analytics.html UI with new tier columns

Organizations are matched to Slack users by email domain, so
"Registered" tier now includes organizations with Slack-only users
who haven't created site accounts.

Co-authored-by: Claude Opus 4.5 <noreply@anthropic.com>
- Add impressions field to package-request.json and update-media-buy-request.json
- Add paused field to package-request.json (default: false)
- Update create_media_buy and update_media_buy documentation
- Fix jest missing @jest/test-sequencer dependency

These fields were defined in core/package.json but missing from request schemas,
making it impossible to set impression goals or initial paused state.
…-package-requests

fix: Add missing impressions and paused fields to package request schemas
@BaiyuScope3 BaiyuScope3 requested a review from bokelley January 13, 2026 23:10
@BaiyuScope3 BaiyuScope3 merged commit 131d3f9 into 2.6.x Jan 13, 2026
11 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants