Skip to content

feat(sharing): per-agent custom instructions for public & channel chats (#1205)#1337

Merged
vybe merged 3 commits into
devfrom
feature/1205-public-channel-prompt
Jul 2, 2026
Merged

feat(sharing): per-agent custom instructions for public & channel chats (#1205)#1337
vybe merged 3 commits into
devfrom
feature/1205-public-channel-prompt

Conversation

@dolho

@dolho dolho commented Jun 24, 2026

Copy link
Copy Markdown
Contributor

Summary

Adds a per-agent Additional instructions field on the Sharing tab that injects a custom system-prompt fragment into public-facing conversations only — public links, channel chats (Slack/Telegram/WhatsApp), and x402 paid chat. Owners can tailor persona, scope limits, disclaimers, or guardrails for outside audiences without touching how the agent behaves in their own authenticated chats, scheduled work, or agent-to-agent calls. The text-surface counterpart of the existing voice_system_prompt.

Implementation (mirrors voice_system_prompt end-to-end)

Behavior guarantees

  • Strict no-op when unset — zero behavior change for existing agents.
  • A DB lookup failure degrades to the memory block alone — never blocks a chat.
  • Composes correctly with MEM-001 per-user memory (folded into the same caller_prompt).
  • Group chats: applied (group channels are public-facing) — per the AC default.

Acceptance criteria

  • New per-agent field editable from the Sharing tab, save/clear + char limit
  • Appended for public links, channel router, x402 paid chat
  • NOT applied to authenticated chat/session, schedules, loops, a2a
  • Composes with MEM-001 memory block in caller_prompt
  • Owner-only GET/PUT mirroring the voice-prompt pattern
  • Empty/unset is a strict no-op
  • Group-chat behavior explicit (applies to groups)

Testing

30 passed — new test_public_channel_prompt.py (db get/set/clear/isolation on SQLite + Postgres; helper composition: 4 combos + db-error degradation) plus the schema-parity and migrations suites green.

Fixes #1205

🤖 Generated with Claude Code

@dolho
dolho requested a review from vybe June 24, 2026 11:20

@vybe vybe left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Validated via /validate-prtwo blockers (the prompt-injection framing, public/channel/x402 wiring, and tests look solid otherwise):

1. Missing Postgres/Alembic migration (Invariant #3). Adds agent_ownership.public_channel_system_prompt via a SQLite migration (_migrate_agent_ownership_public_channel_prompt) + schema.py/tables.py, but no Alembic revision under src/backend/migrations/versions/. PG deployments won't get the column. (schema-parity only guards the SQLite track — same gap as #1339.)

2. Conflicts with dev after #1317. #1317 restructured SharingPanel.vue (moved operator management to the new Access tab, reframed around Identity Proof / access policy). This PR was built on the old layout, so the SharingPanel.vue / routers/sharing.py conflicts need a rebase onto the redesigned component.

— posted via /validate-pr

@github-actions

Copy link
Copy Markdown

⚠️ Nightly unit-suite check skipped — merge conflict against dev.

Resolve by running git merge dev locally and pushing the result. The next nightly run will re-test once the conflict is gone.

@dolho
dolho force-pushed the feature/1205-public-channel-prompt branch from fadcb90 to a8565ad Compare June 25, 2026 14:37
@dolho

dolho commented Jun 25, 2026

Copy link
Copy Markdown
Contributor Author

Rebased onto dev + Alembic fix + review

Fix (rebase): resolved 8 conflicts. All backend ones were the same additive collision — dev's voice_name (#28) vs this PR's public_channel_system_prompt (#1205) at the same spots:

  • schema.py/tables.py/database.py/migrations.py (list): unioned (both columns/methods/entries).
  • migrations.py (function) + db/agents.py (getter/setter): git had interleaved the two functions/methods into one tangled flow — reconstructed both as complete separate units.
  • routers/sharing.py: merged imports (no dup from models) + kept both /access (Client/Viewer User Role (AUTH-002) #17) and /public-prompt GET+PUT (Per-agent custom instructions for public & channel chats (Sharing tab) #1205).
  • SharingPanel.vue: dropped the re-inherited Team Sharing section (Client/Viewer User Role (AUTH-002) #17 moved it to AccessPanel; its shareWithUser/shareEmail no longer exist), kept the new Additional Instructions section.
  • architecture.md: kept both endpoint rows + both DDL lines.

Added missing Alembic revision (fix): the PR predates Alembic adoption (#1183) and shipped SQLite-only — PG-broken (Invariant #9). Added 0005_agent_ownership_public_channel_prompt (chained after 0004_voice_name, ADD COLUMN IF NOT EXISTS).

Review — looks good:

  • Injection boundary is correct: build_public_channel_caller_prompt is wired only into public.py (links), paid.py (x402), message_router.py (channels) — not authenticated chat, loops, schedules, or a2a. ✅ Matches the documented "outside audiences only" intent.
  • Owner-only endpoints, 4000-char cap, empty clears the value.

Verified: py_compile (all backend conflict files), SharingPanel.vue compiles, schema-parity + test_public_channel_prompt.py: 13 passed. Now MERGEABLE.

⚠️ Two coordination flags

  1. SharingPanel vs the reframe (same as feat(sharing): external client roster on the Sharing tab (trinity-enterprise#20) #1335): the Additional Instructions section sits on the pre-reframe Sharing tab. My open feat(ui): reframe Sharing tab to external-client sharing via channels #1347 (Unified Executions Dashboard (EXEC-022) #18) / feat(ui): move channel config into dialogs + VoIP surface (#19) #1348 (MCP Execution Query Tools (MCP-007) #19) rewrite SharingPanel.vue — this will re-conflict. Recommend stacking after Unified Executions Dashboard (EXEC-022) #18/MCP Execution Query Tools (MCP-007) #19.
  2. Alembic 0005 collision: feat(loops): loop-level wall-clock deadline (max_duration_seconds) (#1156) #1182 (loop max_duration) also adds a 0005 off 0004_voice_name. Whichever of feat(loops): loop-level wall-clock deadline (max_duration_seconds) (#1156) #1182/feat(sharing): per-agent custom instructions for public & channel chats (#1205) #1337 merges second must bump to 0006.

@dolho

dolho commented Jun 26, 2026

Copy link
Copy Markdown
Contributor Author

Rebased onto dev — Additional Instructions grafted into the reframed Sharing tab

Both blockers resolved.

1. Postgres/Alembic migration ✅ (already added)

0005_agent_ownership_public_channel_prompt (down_revision = 0004_agent_ownership_voice_name, ADD COLUMN IF NOT EXISTS) — PG deployments get agent_ownership.public_channel_system_prompt. SQLite + schema.py + tables.py consistent.

2. SharingPanel conflict ✅ (re-integrated into the reframe)

Re-resolved the semantic SharingPanel.vue conflict after the #1317/#18/#19 reframe ("Share this agent" → access policy → Channels disclosures → Distribution):

  • Removed the obsolete pieces the old branch carried (Additional Instructions wedged between the Slack/Telegram disclosures + the re-inherited standalone SlackChannelPanel) so dev's ChannelDisclosure channel list stays intact.
  • Added "Additional instructions — public & channel chats only" as its own section after Channels, before Distribution, restyled to the reframe's h4 section header. Textarea + 4000 char counter + Save/Clear unchanged.
  • Script: kept dev's accessMode computed and the public-prompt state/handlers. Also re-added the useAgentsStore import + agentsStore declaration that the 3-way merge dropped (they back fetch/savePublicChannelPrompt); loadPublicPrompt() stays wired into the agentName watch.

Backend (message_router.py/public.py/paid.py injection, owner-only GET/PUT /public-prompt) auto-merged clean — injection boundary unchanged (public/channel/x402 only; never authenticated chat, loops, schedules, a2a).

Verification

  • vue/compiler-sfc parse + compileScriptSFC compiles clean, no markers.
  • test_public_channel_prompt + test_alembic_parity_guard + test_schema_parity37 passed.

Now mergeable.

⚠️ Alembic numbering note

0005 here still collides with #1182 (0005_agent_loops_max_duration) and #1339 (0005_agent_loops_failure_policy), all off 0004. First to merge keeps 0005; the rest bump to the next free number off the new head at merge time.

dolho and others added 2 commits June 30, 2026 13:20
…ts (#1205)

Owners can attach extra system-prompt instructions that apply to public-facing
conversations ONLY — public links, channel chats (Slack/Telegram/WhatsApp), and
x402 paid chat — without changing the agent's behavior in their own
authenticated chats, scheduled runs, loops, or agent-to-agent calls. The
text-surface counterpart of voice_system_prompt.

- schema/migration: agent_ownership.public_channel_system_prompt TEXT (schema.py
  + tables.py Core object + versioned migration, Invariant #3)
- db: get/set_public_channel_system_prompt (set strips, empty clears) + facade
  delegation
- models: PublicChannelPrompt / PublicChannelPromptUpdate (4000-char cap)
- api: owner-only GET/PUT /api/agents/{name}/public-prompt (sharing.py),
  mirroring the voice-prompt endpoints
- injection: platform_prompt_service.build_public_channel_caller_prompt folds the
  fragment with the MEM-001 memory block (public fragment first); wired into the
  three public-facing sites — message_router (channels), public.py (sync+async),
  paid.py (x402). Authenticated chat / schedules / loops / a2a never call it, so
  the scope exclusion holds by construction. Unset = strict no-op; a DB error
  degrades to the memory block (never blocks a chat)
- ui: Additional Instructions textarea (save/clear, char counter) in
  SharingPanel.vue via two agents-store methods
- tests: db get/set/clear/isolation + helper composition (4 combos + db-error
  degradation), SQLite + Postgres; schema-parity + migrations suites green
- docs: requirements section 44, architecture endpoint + column

Related to #1205

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
…em_prompt (#1205)

The PR predates Alembic adoption (#1183) and shipped only the SQLite migration.
Add the paired Postgres revision (0005, chained after 0004_agent_ownership_voice_name)
so the dual-track migration invariant holds — ADD COLUMN IF NOT EXISTS, no-op when
0001_baseline already created the column.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
@dolho
dolho force-pushed the feature/1205-public-channel-prompt branch from 81cbb3f to 53d5c1e Compare June 30, 2026 10:22
@dolho
dolho requested a review from vybe June 30, 2026 11:03
@vybe

vybe commented Jul 2, 2026

Copy link
Copy Markdown
Contributor

Re-review after today's dev movement. The design is sound and your earlier two blockers (missing Alembic revision, SharingPanel.vue reframe conflict) were correctly addressed — but dev advanced today and #1335 (external client roster) just merged, so this PR needs one more rebase before it can land. Test-merged onto current dev; here's exactly what's needed:

Blockers (conflicts vs current dev):

  1. docs/memory/requirements.md — the monolith was split into requirements/*.md by docs: split monolithic requirements.md into per-area sub-files and reconcile dev-process references #1406/docs: split monolithic requirements.md into per-area sub-files + reconcile references (#1406) #1412 (merged earlier today). Take dev's index version and relocate "Section 46. Public & Channel Custom Instructions (Per-agent custom instructions for public & channel chats (Sharing tab) #1205)" into requirements/public-access.md (same area file where Audit Trail System (SEC-001) #20's roster requirement now lives after feat(sharing): external client roster on the Sharing tab (trinity-enterprise#20) #1335) — don't re-add it to the retired monolith.
  2. src/backend/enterprise (submodule) — the branch pins a stale feature/46-mcp-connector commit (eecc98e6…) that only exists on that unmerged enterprise branch and is unrelated to Per-agent custom instructions for public & channel chats (Sharing tab) #1205. Resolve by taking dev's current pointer.
  3. src/backend/routers/sharing.py + src/frontend/src/components/SharingPanel.vue — overlap with feat(sharing): external client roster on the Sharing tab (trinity-enterprise#20) #1335 (just merged). Both PRs add an independent Sharing-tab endpoint/section; keep both (the roster and the Additional-Instructions surfaces are independent).
  4. docs/memory/architecture.md — endpoint-table row overlap with feat(sharing): external client roster on the Sharing tab (trinity-enterprise#20) #1335; keep both rows.

Migration (no action needed here): your 0011_agent_ownership_public_channel_prompt.py (down_revision = 0010_agent_ownership_mcp_exposed) is fine — dev's head is 0010, so you claim 0011 cleanly. Heads-up for #1397: it adds a 0011_* revision on the same parent, so #1397 must renumber to 0012 when it rebases (whichever lands second renumbers; this one is first).

Non-blocking quality:

After the rebase + green CI, re-request me and I'll merge. Context: #1421 (P0 alembic width fix) and #1335 both merged to dev today.

/#1335/#1406 conflicts

Resolutions:
- requirements.md: take dev's index (#1406 split); relocate the #1205 section
  into requirements/public-access.md as Section 47 (46 was taken by #1335's
  roster requirement)
- Alembic: renumber 0011_agent_ownership_public_channel_prompt -> 0012,
  re-parent on 0011_agent_ownership_tts_voice (#1397 landed first; whichever
  lands second renumbers)
- db/migrations.py: keep both MIGRATIONS entries (tts_voice + public_channel_prompt)
- routers/sharing.py + SharingPanel.vue + architecture.md: keep both #1335
  roster and #1205 public-prompt surfaces (independent features)
- .claude + src/backend/enterprise submodules: reset to dev's pointers (the
  branch pinned unrelated/stale commits)

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
@vybe vybe added the ui PR touches the frontend UI — triggers Playwright e2e tests label Jul 2, 2026
@vybe

vybe commented Jul 2, 2026

Copy link
Copy Markdown
Contributor

PR Validation Report (/validate-pr) — post-resolution re-review

PR: #1337 — feat(sharing): per-agent custom instructions for public & channel chats
Author: dolho · Branch: feature/1205-public-channel-promptdev · Files: 17 (+428/−6 vs current dev)

Both earlier blockers and today's 13:30 conflict list are now fully addressed — I resolved the rebase on-branch (merge commit 1c14fe61):

Category Status Notes
Commit Messages conventional; merge commit documents every resolution
Base Branch targets dev; now MERGEABLE
Status Auto-Promotion body rephrased to Fixes #1205 (same-repo) → auto-promotes to status-in-dev on merge, closes at release
Requirements relocated into requirements/public-access.md as Section 47 (46 was taken by #1335's roster); monolith untouched per #1406 split
Architecture endpoint row kept alongside #1335's /clients row
Alembic / dual-track renumbered 0011 → 0012_agent_ownership_public_channel_prompt, re-parented on 0011_agent_ownership_tts_voice (#1397 landed first) — single linear head; SQLite entry ordered after dev's
Submodules .claude + src/backend/enterprise reset to dev's pointers (branch had pinned unrelated/stale commits) — zero submodule delta in the final diff
Security Check no keys/emails/IPs/secrets; no new env vars (no compose wiring needed); no new top-level modules
Feature overlap #1335 roster + #1205 prompt coexist in sharing.py / SharingPanel.vue (independent surfaces; both load in the mount Promise.all)
Test Adequacy ✅/⚠️ DB + composition layers well covered (SQLite+PG, error degradation); endpoint-level 403/422 test still a non-blocking follow-up
Code Quality mirrors voice_system_prompt end-to-end; Invariants #1/#2/#6/#7/#8/#14 hold; strict no-op when unset

ui label added so frontend-e2e exercises the SharingPanel.vue change.

Recommendation: APPROVE — merging on green CI.

@vybe vybe left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Re-review complete — both original blockers fixed by the author, and I resolved the dev rebase on-branch (1c14fe6: requirements relocation to public-access.md §47, Alembic 0012 renumber, #1335 coexistence, submodule pointers reset). Full CI green incl. frontend-e2e + pg-migrations. See validation report above. Merging.

@vybe
vybe merged commit b19d31a into dev Jul 2, 2026
23 checks passed
vybe pushed a commit that referenced this pull request Jul 3, 2026
- migrations.py, test_25_telegram_voice_out.py: parent-squash echoes; PR does
  not touch them -> take dev's versions (keeps #1337 public_channel_prompt
  migration and #1397's final test set)
- TelegramChannelPanel.vue: dev == feature/25 tip here; keep this branch's
  intended replacement of the inline voice block with shared VoiceRepliesControl
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

ui PR touches the frontend UI — triggers Playwright e2e tests

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants