Skip to content

feat(settings): add Hermes agent persona editor (SOUL.md)#16

Merged
jellydn merged 5 commits into
mainfrom
issue-12
Jun 6, 2026
Merged

feat(settings): add Hermes agent persona editor (SOUL.md)#16
jellydn merged 5 commits into
mainfrom
issue-12

Conversation

@jellydn

@jellydn jellydn commented Jun 6, 2026

Copy link
Copy Markdown
Owner

What

Add a Settings-page editor for the user's durable Hermes persona, backed by SOUL.md.

  • Replace the Settings stub with a persona editor (textarea, save, deploy, status panel)
  • Add hermes_settings table and Drizzle migration (0011_spicy_the_phantom.sql)
  • Add POST /api/settings/persona and POST /api/settings/persona/deploy handlers
  • Add SSH helper that base64-encodes persona content and writes /root/.hermes/SOUL.md
  • Add server, routing, and UI tests

Closes #12

Why

Hermes uses SOUL.md as the primary agent identity and slot #1 system prompt source. Users need a durable way to define their agent persona in HermesHub and push it to their already-deployed Telegram Hermes server without manual SSH edits.

How

  • Persistence: One hermes_settings row per user (upsert on user_id) stores agent_persona and deploy metadata.
  • Save: Validates non-empty content and a 20,000-character cap, then upserts with an audit log.
  • Deploy: Resolves the existing Telegram deployment target (getTelegramDeployInfo), writes SOUL.md over SSH via base64 transfer to avoid shell injection, restarts the Hermes gateway, and records deploy metadata in a transaction.
  • UI: Mirrors provider settings layout — main editor + aside for saved/deploy state. Deploy is gated until a persona is saved and Telegram deploy exists.
  • Security: Mutating endpoints use the existing production HTTPS middleware.

Checklist

  • Tests added or updated
  • Documentation updated
  • No breaking changes (or breaking changes documented above)

Summary by CodeRabbit

  • New Features
    • Added ability to create, save, and manage an agent persona configuration for your AI assistant.
    • Added persona deployment functionality to sync persona settings with Hermes servers.
    • Integrated persona management into the settings page with character limits and deployment status tracking.

@changeset-bot

changeset-bot Bot commented Jun 6, 2026

Copy link
Copy Markdown

⚠️ No Changeset found

Latest commit: 6d7ac49

Merging this PR will not cause a version bump for any packages. If these changes should not result in a new version, you're good to go. If these changes should result in a version bump, you need to add a changeset.

Click here to learn what changesets are, and how to add one.

Click here if you're a maintainer who wants to add a changeset to this PR

@coderabbitai

coderabbitai Bot commented Jun 6, 2026

Copy link
Copy Markdown

Review Change Stack

Warning

Review limit reached

@jellydn, we couldn't start this review because you've reached your PR review rate limit.

More reviews will be available in 26 minutes and 20 seconds. Learn how PR review limits work.

Your organization has run out of usage credits. Purchase more in the billing tab.

⌛ How to resolve this issue?

After more reviews become available, a review can be triggered using the @coderabbitai review command as a PR comment. Alternatively, push new commits to this PR.

We recommend that you space out your commits to avoid hitting the rate limit.

🚦 How do rate limits work?

CodeRabbit enforces hourly rate limits for each developer per organization.

Our paid plans include higher PR review limits than trial, open-source, and free plans. In all cases, reviews become available again over time. During sustained high-volume PR review activity, CodeRabbit may temporarily slow when the next review becomes available.

Please see our Fair Usage Limits Policy for further information.

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

Run ID: 41232a94-0ae2-4b05-bcc6-5c0f49f44c19

📥 Commits

Reviewing files that changed from the base of the PR and between c988e86 and 6d7ac49.

📒 Files selected for processing (30)
  • CONTEXT.md
  • README.md
  • docs/api-reference.md
  • drizzle/0011_spicy_the_phantom.sql
  • drizzle/0012_pink_tattoo.sql
  • drizzle/meta/0011_snapshot.json
  • drizzle/meta/0012_snapshot.json
  • drizzle/meta/_journal.json
  • server/app.test.ts
  • server/app.ts
  • server/db/schema.ts
  • server/deploy.ts
  • server/hermes/persona.test.ts
  • server/hermes/persona.ts
  • server/hermes/telegram-deploy-context.test.ts
  • server/hermes/telegram-deploy-context.ts
  • server/settings.test.ts
  • server/settings.ts
  • server/settings/config.test.ts
  • server/settings/config.ts
  • server/settings/records.ts
  • src/features/providers/provider-settings-aside.tsx
  • src/features/providers/provider-settings.tsx
  • src/features/settings/persona-settings-aside.tsx
  • src/features/settings/persona-settings.test.tsx
  • src/features/settings/persona-settings.tsx
  • src/features/settings/settings-page.tsx
  • src/lib/load-telegram-deploy.ts
  • src/routes/ai-provider.tsx
  • src/routes/settings.tsx

Walkthrough

This PR introduces complete agent persona management to Hermes Hub. Users can edit and save persona text (up to 20,000 characters), deploy it to a managed Hermes server via SSH, and track deployment status. The feature includes database persistence, SSH-based file writes with base64 encoding for safe transfer, audit logging, and a full React UI with loading states and error handling.

Changes

Agent Persona Settings

Layer / File(s) Summary
Database schema and migrations
drizzle/0011_spicy_the_phantom.sql, drizzle/0012_pink_tattoo.sql, drizzle/meta/0011_snapshot.json, drizzle/meta/0012_snapshot.json, drizzle/meta/_journal.json, server/db/schema.ts
New hermes_settings table stores persona text, indexed on userId with cascade delete. Initial migration includes deployment columns that are later removed in migration 0012.
Persona SSH persistence and validation
server/hermes/persona.ts, server/hermes/persona.test.ts
Validates persona content (trim, max 20,000 characters), builds base64-encoded shell commands for safe remote writes, and executes SSH commands to persist SOUL.md to the Hermes volume.
Settings config parsing and database records
server/settings/config.ts, server/settings/config.test.ts, server/settings/records.ts
Parses persona save request bodies, validates content, and provides database query/upsert operations for hermesSettings. Includes type definitions for PersonaSettingsSummary.
Telegram Hermes deployment context
server/hermes/telegram-deploy-context.ts, server/hermes/telegram-deploy-context.test.ts, server/deploy.ts
Extracts and reuses deployment context resolution logic: combines Telegram deployment info with SSH context, handles guard failures, and early-returns errors. Refactors deployProviderToHermes to use the new resolver.
Backend persona settings endpoints
server/settings.ts, server/settings.test.ts
Implements savePersonaSettings (POST) and deployPersonaToHermes (POST) handlers. Both authenticate, validate, log audits, clear cache, and return structured responses. Deploy handler manages SSH writes, Hermes restart, and failure logging with best-effort audit entries.
API routes and integration
server/app.ts, server/app.test.ts
Registers HTTPS-protected routes /api/settings/persona and /api/settings/persona/deploy. Tests verify handler invocation and production HTTP rejection (426).
Frontend persona UI components
src/features/settings/persona-settings.tsx, src/features/settings/persona-settings-aside.tsx, src/features/settings/persona-settings.test.tsx
PersonaSettings manages persona textarea and save flow. PersonaSettingsAside displays saved state and deploy button with loading/error handling. Full test coverage for rendering, save, and deploy workflows.
Shared Telegram deployment type and loaders
src/lib/load-telegram-deploy.ts, src/features/providers/provider-settings.tsx, src/features/providers/provider-settings-aside.tsx
Extracts TelegramDeployInfo type and loadTelegramDeploy server function for consistent Telegram deployment data across routes. Updates provider settings to use shared type.
Route data loading
src/routes/settings.tsx, src/routes/ai-provider.tsx
Settings route loads persona settings via new loadPersonaSettings server function and integrates PersonaSettings component. AI provider route refactored to use shared loadTelegramDeploy instead of local implementation.

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~25 minutes

Possibly related PRs

  • jellydn/hermes-hub#5: Refactors deployProviderToHermes to use the new resolveTelegramHermesDeployContext helper introduced in this PR for cleaner deployment context resolution.

Poem

🐰 A persona takes shape in digital dreams,
SOUL.md whispers through SSH streams,
Base64 encodes what the heart would say,
Hermes listens, deployed today!
Settings saved, now let the agent play. ✨

🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 0.00% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (4 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title 'feat(settings): add Hermes agent persona editor (SOUL.md)' directly and accurately describes the main change—introducing a persona editor for Hermes agent identity backed by SOUL.md file management.
Linked Issues check ✅ Passed The PR successfully implements the core requirements for issue #12: adds a persona editor UI, creates database persistence via hermes_settings table, provides save/deploy API endpoints, and includes comprehensive server and UI tests.
Out of Scope Changes check ✅ Passed All changes are within scope of adding a persona editor and related infrastructure. The refactoring of deploy context and removal of redundant deployment columns align with consolidating the persona deployment flow and are directly necessary to support the new feature.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch issue-12

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

@github-actions

github-actions Bot commented Jun 6, 2026

Copy link
Copy Markdown

React Doctor

No React Doctor issues found in this scan.

Score Issues Errors Warnings Affected Files Scope
100 / 100 (Great) 0 0 0 0 30 files changed on issue-12 vs. main

View workflow run

Generated by React Doctor. Questions? Contact founders@million.dev.

@gemini-code-assist gemini-code-assist Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Code Review

This pull request introduces the ability to configure and deploy a custom agent persona (saved to SOUL.md on the deployed Hermes server). It adds the hermes_settings database table, backend endpoints for saving and deploying the persona, and a frontend UI with state management and tests. The review feedback highlights two important robustness issues in server/settings.ts: a potential database query error if deployedServerId is null, and a potential TypeError if the parsed JSON payload itself is null.

Important

The consumer version of Gemini Code Assist on GitHub is being sunset. Starting June 18, 2026, new organization installations will be blocked, and all code review activity will officially cease on July 17, 2026.
For more details on the timeline and next steps, please review the Help Documentation.

Comment thread server/settings.ts Outdated
Comment on lines +102 to +111
const telegramInfo = await getTelegramDeployInfo(session.user.id);
if (!telegramInfo) {
return context.json(
{
error:
"No Hermes deployment found. Deploy a Telegram bot to a server first.",
},
400,
);
}

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

high

In the database schema, deployedServerId is a nullable field in the telegram_configs table. If a user has configured a Telegram bot but has not yet deployed it, telegramInfo will be returned but telegramInfo.deployedServerId will be null. Passing null to requireOwnedServerSshById can lead to database query errors or unexpected runtime exceptions. Adding a check to ensure deployedServerId is present prevents this issue.

Suggested change
const telegramInfo = await getTelegramDeployInfo(session.user.id);
if (!telegramInfo) {
return context.json(
{
error:
"No Hermes deployment found. Deploy a Telegram bot to a server first.",
},
400,
);
}
const telegramInfo = await getTelegramDeployInfo(session.user.id);
if (!telegramInfo || !telegramInfo.deployedServerId) {
return context.json(
{
error:
"No Hermes deployment found. Deploy a Telegram bot to a server first.",
},
400,
);
}

Comment thread server/settings.ts Outdated
Comment on lines +42 to +44
if (typeof payload.agentPersona !== "string") {
return context.json({ error: "Persona content is required." }, 400);
}

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

medium

If the request payload is null (which can happen if the client sends a null JSON body), accessing payload.agentPersona will throw a TypeError: Cannot read properties of null (reading 'agentPersona'). Adding a null check for payload prevents potential unhandled exceptions and returns a clean 400 error.

Suggested change
if (typeof payload.agentPersona !== "string") {
return context.json({ error: "Persona content is required." }, 400);
}
if (!payload || typeof payload.agentPersona !== "string") {
return context.json({ error: "Persona content is required." }, 400);
}

@coderabbitai

coderabbitai Bot commented Jun 6, 2026

Copy link
Copy Markdown

Note

Unit test generation is a public access feature. Expect some limitations and changes as we gather feedback and continue to improve it.


Generating unit tests... This may take up to 20 minutes.

@coderabbitai

coderabbitai Bot commented Jun 6, 2026

Copy link
Copy Markdown

✅ Created PR with unit tests: #17

@jellydn jellydn marked this pull request as ready for review June 6, 2026 12:40

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Actionable comments posted: 2

🧹 Nitpick comments (3)
server/settings/config.test.ts (1)

5-35: ⚡ Quick win

Add boundary tests for the 20,000-character persona limit.

This suite covers required/trim behavior, but it misses the explicit size contract from this PR. Please add tests for exactly 20,000 chars (accept) and 20,001 chars (reject) to lock the API contract and prevent silent regressions.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@server/settings/config.test.ts` around lines 5 - 35, Add two boundary tests
for parsePersonaSaveBody to enforce the 20,000-character limit: create one test
that passes a string of exactly 20,000 non-space characters (optionally with
surrounding spaces to ensure trimming) and assert it returns { ok: true,
content: <trimmed string> }, and another test that passes a string of 20,001
characters and assert it returns { ok: false, error: "Persona content exceeds
maximum length." } (or the existing error message used by parsePersonaSaveBody).
Place these tests alongside the existing parsePersonaSaveBody tests to lock the
size contract.
server/settings/config.ts (1)

8-21: 💤 Low value

Consider adding explicit array rejection.

The current check !payload || typeof payload !== "object" accepts arrays (since typeof [] === "object"). While the subsequent type check on line 16 would correctly reject arrays (because [].agentPersona is undefined), adding an explicit Array.isArray(payload) check would make the intent clearer and prevent potential confusion.

♻️ Suggested refinement
 export function parsePersonaSaveBody(
 	payload: unknown,
 ): { ok: true; content: string } | { ok: false; error: string } {
-	if (!payload || typeof payload !== "object") {
+	if (!payload || typeof payload !== "object" || Array.isArray(payload)) {
 		return { ok: false, error: "Persona content is required." };
 	}
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@server/settings/config.ts` around lines 8 - 21, The payload object check in
parsePersonaSaveBody currently treats arrays as objects; update the initial
guard to also reject arrays (use Array.isArray(payload)) so arrays are treated
as invalid input, then return the same { ok: false, error: "Persona content is
required." } path; keep the rest of the function (reading (payload as {
agentPersona?: unknown }).agentPersona and calling
validateAgentPersona(agentPersona)) unchanged.
src/features/settings/persona-settings.test.tsx (1)

56-159: ⚡ Quick win

Add test coverage for error scenarios.

The current test suite covers happy paths (successful save, successful deploy, button gating), but doesn't verify error handling. Adding tests for error responses and network failures would ensure the error UI works correctly and would have caught the missing catch blocks flagged in the production code.

📋 Suggested error test cases

Add tests after line 104 for save errors:

it("shows error message when save fails", async () => {
  fetchMock.mockResolvedValueOnce(
    new Response(
      JSON.stringify({ error: "Persona content cannot be empty." }),
      { status: 400, headers: { "content-type": "application/json" } }
    )
  );

  render(<PersonaSettings initialSettings={null} />);

  fireEvent.change(screen.getByLabelText(/persona content/i), {
    target: { value: "" },
  });
  fireEvent.click(screen.getByRole("button", { name: /save persona/i }));

  await flushAsyncWork();

  expect(screen.getByText(/persona content cannot be empty/i)).toBeTruthy();
});

it("shows error message when save network fails", async () => {
  fetchMock.mockRejectedValueOnce(new Error("Network error"));

  render(<PersonaSettings initialSettings={null} />);

  fireEvent.change(screen.getByLabelText(/persona content/i), {
    target: { value: "Test" },
  });
  fireEvent.click(screen.getByRole("button", { name: /save persona/i }));

  await flushAsyncWork();

  expect(screen.getByText(/network error/i)).toBeTruthy();
});

Add tests after line 144 for deploy errors:

it("shows error message when deploy fails", async () => {
  fetchMock.mockResolvedValueOnce(
    new Response(
      JSON.stringify({ error: "No persona saved." }),
      { status: 400, headers: { "content-type": "application/json" } }
    )
  );

  render(
    <PersonaSettings
      initialSettings={{
        agentPersona: "You are Hermes.",
        updatedAt: "2026-06-06T12:00:00.000Z",
      }}
      telegramDeploy={{ deployedServerHost: "1.2.3.4" }}
    />
  );

  fireEvent.click(screen.getByRole("button", { name: /deploy to hermes server/i }));
  await flushAsyncWork();

  expect(screen.getByText(/no persona saved/i)).toBeTruthy();
});
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@src/features/settings/persona-settings.test.tsx` around lines 56 - 159, Add
tests to persona-settings.test.tsx that cover error paths for PersonaSettings:
write two save-error tests that mock fetchMock to return a 400 JSON error
response and to reject (network error) and assert the error text appears after
firing change + clicking the "Save persona" button and awaiting flushAsyncWork;
and write a deploy-error test that mocks a 400 JSON error response for the POST
to "/api/settings/persona/deploy", clicks the "Deploy to Hermes server" button
on the rendered PersonaSettings (with telegramDeploy provided), awaits
flushAsyncWork, and asserts the returned error message is displayed; reuse
existing helpers (render, fireEvent, fetchMock, flushAsyncWork, screen) and
assert via screen.getByText for the expected error strings.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@src/features/settings/persona-settings-aside.tsx`:
- Around line 25-59: The handleDeploy function currently only uses try-finally
so a thrown fetch() (network/CORS) will bypass setting an error; wrap the
fetch+response parsing in a try-catch inside handleDeploy, and in the catch call
setDeployError with a user-friendly message (and optionally include
error.message), ensure setIsDeploying is still cleared in the existing finally,
and avoid setting deployResult when an error occurs; update references:
handleDeploy, setDeployError, setDeployResult, setIsDeploying and the
fetch("/api/settings/persona/deploy")/response.json parsing logic.

In `@src/features/settings/persona-settings.tsx`:
- Around line 30-60: The handleSave function currently only uses try/finally and
swallows fetch() rejections, so wrap the network call in a try/catch (inside the
existing handleSave) to catch errors from fetch() and response.json() that
bubble up, call setSaveMessage({ type: "error", text: <friendly message> }) with
the error message (or a generic "Network error while saving persona") and ensure
setIsSaving is still cleared in the existing finally; update references inside
handleSave to use the caught error when logging or setting the save message so
users see a failure if fetch throws.

---

Nitpick comments:
In `@server/settings/config.test.ts`:
- Around line 5-35: Add two boundary tests for parsePersonaSaveBody to enforce
the 20,000-character limit: create one test that passes a string of exactly
20,000 non-space characters (optionally with surrounding spaces to ensure
trimming) and assert it returns { ok: true, content: <trimmed string> }, and
another test that passes a string of 20,001 characters and assert it returns {
ok: false, error: "Persona content exceeds maximum length." } (or the existing
error message used by parsePersonaSaveBody). Place these tests alongside the
existing parsePersonaSaveBody tests to lock the size contract.

In `@server/settings/config.ts`:
- Around line 8-21: The payload object check in parsePersonaSaveBody currently
treats arrays as objects; update the initial guard to also reject arrays (use
Array.isArray(payload)) so arrays are treated as invalid input, then return the
same { ok: false, error: "Persona content is required." } path; keep the rest of
the function (reading (payload as { agentPersona?: unknown }).agentPersona and
calling validateAgentPersona(agentPersona)) unchanged.

In `@src/features/settings/persona-settings.test.tsx`:
- Around line 56-159: Add tests to persona-settings.test.tsx that cover error
paths for PersonaSettings: write two save-error tests that mock fetchMock to
return a 400 JSON error response and to reject (network error) and assert the
error text appears after firing change + clicking the "Save persona" button and
awaiting flushAsyncWork; and write a deploy-error test that mocks a 400 JSON
error response for the POST to "/api/settings/persona/deploy", clicks the
"Deploy to Hermes server" button on the rendered PersonaSettings (with
telegramDeploy provided), awaits flushAsyncWork, and asserts the returned error
message is displayed; reuse existing helpers (render, fireEvent, fetchMock,
flushAsyncWork, screen) and assert via screen.getByText for the expected error
strings.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

Run ID: afc8a41a-85cc-466c-866d-108316422181

📥 Commits

Reviewing files that changed from the base of the PR and between 6016bb4 and c988e86.

📒 Files selected for processing (27)
  • drizzle/0011_spicy_the_phantom.sql
  • drizzle/0012_pink_tattoo.sql
  • drizzle/meta/0011_snapshot.json
  • drizzle/meta/0012_snapshot.json
  • drizzle/meta/_journal.json
  • server/app.test.ts
  • server/app.ts
  • server/db/schema.ts
  • server/deploy.ts
  • server/hermes/persona.test.ts
  • server/hermes/persona.ts
  • server/hermes/telegram-deploy-context.test.ts
  • server/hermes/telegram-deploy-context.ts
  • server/settings.test.ts
  • server/settings.ts
  • server/settings/config.test.ts
  • server/settings/config.ts
  • server/settings/records.ts
  • src/features/providers/provider-settings-aside.tsx
  • src/features/providers/provider-settings.tsx
  • src/features/settings/persona-settings-aside.tsx
  • src/features/settings/persona-settings.test.tsx
  • src/features/settings/persona-settings.tsx
  • src/features/settings/settings-page.tsx
  • src/lib/load-telegram-deploy.ts
  • src/routes/ai-provider.tsx
  • src/routes/settings.tsx

Comment thread src/features/settings/persona-settings-aside.tsx
Comment thread src/features/settings/persona-settings.tsx
jellydn added 4 commits June 6, 2026 20:54
Replace the Settings stub with a persona editor that persists content in
hermes_settings and deploys it to the Telegram-deployed Hermes server via
SSH SOUL.md writes and gateway restarts.
Reject null JSON save payloads before property access and require a
deployed Telegram server ID before resolving SSH credentials.
Extract shared Telegram deploy context resolution and route loader,
drop duplicate hermes_settings deploy metadata, unify persona contracts,
and replace the reducer with a simpler aside-based settings layout.
fetch() rejections bypassed the try/finally handlers, leaving users with
no feedback when the request never completed. Catch network failures and
show a friendly error while still clearing loading state in finally.
Add Settings endpoints to the API reference, feature bullets and route
map in README, and Agent Persona glossary entry in CONTEXT.md for the
persona editor introduced in issue #12.

Co-Authored-By: Warp <agent@warp.dev>
@jellydn jellydn merged commit 94f640a into main Jun 6, 2026
8 checks passed
jellydn added a commit that referenced this pull request Jun 6, 2026
- Document optional serverId on persona and MCP deploy endpoints
- Replace outdated Telegram-linked deploy language with install-based targets
- Add MCP presets, mcp_servers schema row, and Hermes Deploy Target glossary term
- Fix duplicate audit_logs row in API reference

Triggered by issue-22 features: persona editor (#16), MCP manager (#21),
VPS setup check (#19), and deploy target selector.

Co-authored-by: Warp <agent@warp.dev>
@jellydn jellydn deleted the issue-12 branch June 16, 2026 00:32
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.

Add Hermes Agent Persona to setting page

1 participant