Skip to content

(MOT-4173) console: prompts page for the prompt store#565

Open
rohitg00 wants to merge 3 commits into
mainfrom
console-prompts-view
Open

(MOT-4173) console: prompts page for the prompt store#565
rohitg00 wants to merge 3 commits into
mainfrom
console-prompts-view

Conversation

@rohitg00

@rohitg00 rohitg00 commented Jul 22, 2026

Copy link
Copy Markdown
Contributor

What

A prompts page in the console, following the same optional-worker pattern as the memory and github pages:

  • Nav entry appears only while the directory worker is connected (use-prompts-status presence probe over the shared useWorkerPresence hook); a direct #/prompts hit while it is absent lands on the standard install notice.
  • Left rail lists everything the prompt store serves: worker-shipped slash templates and user library entries, each tagged with kind (system highlighted) and source badges.
  • Right pane is the editor: name (locked for existing entries), kind select, description, body. Save and delete write through directory::prompts::save / delete; fork opens an editable copy under <name>-fork. Worker-shipped templates render read-only, since they ship with their worker's bundle.
  • lib/prompts.ts data layer follows memory.ts conventions: zod schemas with defaults for cross-version tolerance, dropped unparseable rows on reads, plain trigger on mutations.

Why

The prompt library backend (#564) gives agents and operators named system prompts; this makes the store visible and editable the way the memory worker's banks are. The new-chat dropdown that applies a chosen prompt to a session is a separate follow-up on the injectable-view base being built independently; this page is the browse-and-edit surface it will pick from.

Validation

  • Typecheck clean; biome clean on every touched file; nav-options tests extended for the new entry (7 pass). The one failing suite in the repo (FunctionCallCard.test.ts, a jsdom userAgent issue in a diff-rendering dependency) and the two biome errors (Configuration tab, index.css) predate this change and touch files this PR does not.
  • Live against a running rig (engine 0.21.6, directory worker with the (MOT-4172) iii-directory: user prompt library backend #564 build): nav tab appeared via presence, the page listed 4 real library entries with kind and source badges, open/edit/save/fork/delete all round-tripped, no console errors.

Dependencies

Read-only listing works against any directory worker. Save, fork, and delete need the library backend from #564; on an older worker those actions surface the worker's own error in the page's alert row.

Linear

Part of MOT-4173 and MOT-4169.

Second slice: apply prompts in chat (added after review of the call notes)

  • Composer toolbar gains a prompt picker next to the memory bank picker: prompt: default (the harness identity chain) or any kind: system entry from the store. Selection fetches the body and every following send in that conversation carries options.system_prompt + system_prompt_strategy: override; switching mid-conversation applies from the next turn. HarnessSendOptions gains the system_prompt_strategy field the harness already accepts.
  • The composer slash menu now lists kind: command prompt-store entries alongside builtins; selecting one injects the prompt BODY into the message (context injection). System prompts are deliberately excluded from the slash menu - they apply through the picker, not the message. The slash trigger pattern now allows kebab-case names.
  • Live-verified on a rig: picked prompt: scraper in the dropdown, asked the chat what its job is, and it answered as the five-line scraper prompt (pipe the fetch, state::set, never spawn) instead of the general agent identity.

Summary by CodeRabbit

  • New Features
    • Added a prompts management page for browsing, editing, saving, forking, and deleting prompts.
    • Added per-conversation system prompt selection for real backend chats.
    • Added prompt-based slash commands, including support for kebab-case names.
    • Added conditional navigation to the prompts page when prompt functionality is available.
  • Bug Fixes
    • Preserves selected prompts in the picker even if they are no longer available in the prompt list.

@vercel

vercel Bot commented Jul 22, 2026

Copy link
Copy Markdown

The latest updates on your projects. Learn more about Vercel for GitHub.

Project Deployment Actions Updated (UTC)
workers Ready Ready Preview, Comment Jul 23, 2026 9:46am
workers-tech-spec Ready Ready Preview, Comment Jul 23, 2026 9:46am

Request Review

@coderabbitai

coderabbitai Bot commented Jul 22, 2026

Copy link
Copy Markdown

Review Change Stack

Warning

Review limit reached

@rohitg00, you've reached your PR review limit, so we couldn't start this review.

Next review available in: 42 minutes

Enable usage-based reviews in Billing to review now. Otherwise, wait until the next included review is available.
You're only billed for reviews past your plan's rate limits ($0.25/file).

How can I continue?

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.

To avoid repeated limits, reduce automatic review volume by pausing incremental auto-reviews earlier, using label-based review opt-in, excluding WIP or generated PR titles, or requesting reviews manually when the PR is ready. If your team needs uninterrupted high-volume reviews, an organization admin can enable usage-based reviews.

How do review limits work?

CodeRabbit enforces per-developer PR review limits for each organization. Most developers receive the normal plan review availability.

For paid Pro and Pro+ PR reviews, CodeRabbit uses adaptive limits for sustained high-volume activity. When a developer's recent PR review activity reaches the 95th percentile or higher among CodeRabbit users, additional reviews become available more gradually as earlier reviews age out of the rolling window.

Please refer docs for additional details.

Review details
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro Plus

Run ID: fa86a9e5-f78b-4c74-bf74-cffd5ea82e5b

📥 Commits

Reviewing files that changed from the base of the PR and between 8c171c7 and 5c5fca4.

📒 Files selected for processing (3)
  • console/web/src/components/chat/PromptPicker.tsx
  • console/web/src/components/chat/lexical/SlashCommandsPlugin.tsx
  • console/web/src/pages/Prompts/index.tsx
📝 Walkthrough

Walkthrough

The console adds a prompts route and editor backed by directory-worker prompt APIs. Real-backend chats can select per-session system prompts, while slash commands dynamically load and inject command prompt bodies.

Changes

Console prompt functionality

Layer / File(s) Summary
Prompt availability and routing
console/web/src/hooks/use-prompts-status.ts, console/web/src/lib/conversations-context.tsx, console/web/src/lib/nav-options.ts, console/web/src/lib/nav-options.test.ts, console/web/src/hooks/use-hash-route.ts, console/web/src/App.tsx
Prompt worker presence now gates navigation and chat capabilities, and the prompts view is routable and rendered.
Prompt store and editor
console/web/src/lib/prompts.ts, console/web/src/pages/Prompts/index.tsx
Typed worker wrappers and a split prompt editor support listing, loading, saving, forking, and deleting prompts.
Session prompt selection and sending
console/web/src/components/chat/PromptPicker.tsx, console/web/src/components/chat/Composer.tsx, console/web/src/components/chat/ChatView.tsx, console/web/src/lib/backend/types.ts, console/web/src/lib/backend/real.ts, console/web/src/lib/backend/harness-send.ts
The composer selects system prompts, and real-backend stream or queued-message requests forward the selected body as an override.
Prompt-backed slash commands
console/web/src/lib/slash-commands.ts, console/web/src/components/chat/lexical/SlashCommandsPlugin.tsx
Command prompts are loaded into slash-command suggestions and asynchronously expanded into editor content, including kebab-case names.

Estimated code review effort: 4 (Complex) | ~45 minutes

Sequence Diagram(s)

sequenceDiagram
  participant User
  participant PromptPicker
  participant PromptStore
  participant ChatView
  participant RealBackend
  User->>PromptPicker: choose system prompt
  PromptPicker->>PromptStore: getPrompt(name)
  PromptStore-->>PromptPicker: prompt body
  PromptPicker->>ChatView: update session prompt
  ChatView->>RealBackend: send systemPrompt override
Loading

Possibly related PRs

  • iii-hq/workers#564: Adds the directory-worker prompt metadata and CRUD backend used by this console UI.
  • iii-hq/workers#496: Uses similar worker-availability gating and console picker wiring patterns.

Suggested reviewers: andersonleal, ytallo

Poem

I’m a rabbit with prompts in my pack,
Editing words and sending them back.
Slash commands hop through the air,
System prompts bloom everywhere.
The console now follows their track!

🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 42.86% 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 is concise and clearly describes the main change: adding a prompts page for the prompt store.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.
✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch console-prompts-view

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.

@github-actions

Copy link
Copy Markdown
Contributor

skill-check — worker

0 verified, 47 skipped (no docs/).

Layer Result
structure
vale
ai
render

Four for four. Nicely done.

@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: 6

🤖 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 `@console/web/src/components/chat/ChatView.tsx`:
- Around line 161-163: Replace the single ChatView-level sessionPrompt state
with prompt selections keyed by conversation.id, preferably in the existing
conversation state. Derive the selected override for the active conversation
before both send paths, so switching conversations neither leaks the previous
selection nor loses each conversation’s prior selection; update the
prompt-switching logic to write to the active conversation’s entry.

In `@console/web/src/components/chat/lexical/SlashCommandsPlugin.tsx`:
- Around line 100-113: Update the async callback in the SlashCommandsPlugin
prompt-loading flow to replace the node only if it still contains the original
`${entry.command} ` placeholder. Preserve the node and all user-entered text
when its content has changed while getPrompt(name) is pending.

In `@console/web/src/components/chat/PromptPicker.tsx`:
- Around line 74-81: Update the PromptPicker onChange handler to invalidate any
in-flight getPrompt request on every selection, including DEFAULT, and track the
latest selection so stale responses cannot call onChange. Handle getPrompt
failures without applying a result, and ensure only the current successful
request updates the selected prompt.

In `@console/web/src/lib/prompts.ts`:
- Around line 17-33: Update the Rust implementations of directory::prompts::list
and directory::prompts::get, including PromptEntry and PromptGetOutput
serialization, to emit each prompt’s actual kind and source fields. Ensure the
values are populated before promptRowSchema or promptDetailSchema defaults are
applied so system prompts retain their true kind and user library prompts retain
their true source and remain editable/deletable.

In `@console/web/src/pages/Prompts/index.tsx`:
- Around line 78-89: Update the open callback to handle both getPrompt failures
and null results through the existing error state: catch rejected requests and
set a useful error message, and set an appropriate error when no prompt detail
is returned instead of returning silently. Preserve the existing draft
population for successful results.
- Around line 132-136: Update onDelete to require explicit user confirmation
before calling deletePrompt, preserving the existing early return and clearing
draft only after successful deletion. Ensure every prompt-deletion entry point,
including the additional onDelete occurrence, uses the same confirmation flow
rather than relying on deletePrompt’s hardcoded yes option.
🪄 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 Plus

Run ID: 6722d1bd-48c0-4aec-9e31-38642143ba1f

📥 Commits

Reviewing files that changed from the base of the PR and between 7ff0617 and 8c171c7.

📒 Files selected for processing (16)
  • console/web/src/App.tsx
  • console/web/src/components/chat/ChatView.tsx
  • console/web/src/components/chat/Composer.tsx
  • console/web/src/components/chat/PromptPicker.tsx
  • console/web/src/components/chat/lexical/SlashCommandsPlugin.tsx
  • console/web/src/hooks/use-hash-route.ts
  • console/web/src/hooks/use-prompts-status.ts
  • console/web/src/lib/backend/harness-send.ts
  • console/web/src/lib/backend/real.ts
  • console/web/src/lib/backend/types.ts
  • console/web/src/lib/conversations-context.tsx
  • console/web/src/lib/nav-options.test.ts
  • console/web/src/lib/nav-options.ts
  • console/web/src/lib/prompts.ts
  • console/web/src/lib/slash-commands.ts
  • console/web/src/pages/Prompts/index.tsx

Comment on lines +161 to +163
// System prompt override for this conversation (prompt store, kind:
// system). Applied to every send until switched back to default.
const [sessionPrompt, setSessionPrompt] = useState<SessionPrompt | null>(null)

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

🎯 Functional Correctness | 🟠 Major | ⚡ Quick win

Scope the selected prompt by conversation.id.

This state can leak the previous chat’s override into the next selected conversation; if ChatView remounts instead, it loses the prior conversation’s selection. Store prompt selection keyed by conversation id (preferably in conversation state) and derive the current override from that mapping before both send paths.

🤖 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 `@console/web/src/components/chat/ChatView.tsx` around lines 161 - 163, Replace
the single ChatView-level sessionPrompt state with prompt selections keyed by
conversation.id, preferably in the existing conversation state. Derive the
selected override for the active conversation before both send paths, so
switching conversations neither leaks the previous selection nor loses each
conversation’s prior selection; update the prompt-switching logic to write to
the active conversation’s entry.

Comment thread console/web/src/components/chat/lexical/SlashCommandsPlugin.tsx
Comment thread console/web/src/components/chat/PromptPicker.tsx Outdated
Comment on lines +17 to +33
const promptRowSchema = z.object({
name: z.string(),
description: z.string().default(''),
kind: z.string().default('command'),
source: z.string().default('worker'),
modified_at: z.string().default(''),
})
export type PromptRow = z.infer<typeof promptRowSchema>

const promptListSchema = z.object({
prompts: z.array(z.unknown()).default([]),
})

const promptDetailSchema = promptRowSchema.extend({
body: z.string().default(''),
})
export type PromptDetail = z.infer<typeof promptDetailSchema>

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

🗄️ Data Integrity & Integration | 🟠 Major | 🏗️ Heavy lift

🧩 Analysis chain

🏁 Script executed:

#!/bin/bash
# Confirm whether directory::prompts::list/get currently emit `kind`/`source`.
fd prompts.rs --full-path iii-directory
rg -n -A5 'struct PromptEntry|struct PromptGetOutput' iii-directory/src/functions/prompts.rs

Repository: iii-hq/workers

Length of output: 580


🏁 Script executed:

#!/bin/bash
set -euo pipefail

echo "== prompts schema/usages =="
sed -n '1,80p' console/web/src/lib/prompts.ts
echo
rg -n "getPrompt|open\\(|existing:|readonly:|kind|source" console/web/src/pages/Prompts/console/web/src -S || true

echo
echo "== iii-directory prompts.rs relevant sections =="
sed -n '1,180p' iii-directory/src/functions/prompts.rs
echo
sed -n '1,140p' iii-directory/tests/steps/prompts.rs

echo
echo "== zod package reference =="
fd -i package.json . -x sh -c 'echo "--- $1"; rg -n "\"zod\"|zod@|zod:" "$1" || true' sh {}

Repository: iii-hq/workers

Length of output: 14413


🏁 Script executed:

#!/bin/bash
set -euo pipefail

echo "== console/prompts page files =="
fd -a "index.tsx|.*\.ts(x)?$" console/web/src/pages 2>/dev/null | grep -i "prompt" || true
echo
if [ -d console/web/src/pages/Prompts ]; then
  rg -n "getPrompt|listPrompts|open\\(|existing:|readonly:|kind|source|delete|editable" console/web/src/pages/Prompts -S
fi
echo
if [ -f console/web/src/lib/prompts.ts ]; then
  sed -n '1,120p' console/web/src/lib/prompts.ts
fi

echo
echo "== zod v4 optional/unknown/object behavior =="
node - <<'JS'
const schema = require('zod').z.object({
  kind: require('zod').z.string().default('command'),
  source: require('zod').z.string().default('worker'),
});
for (const input of [{}, {kind:'system'}, {kind:'system', source:'user'}]) {
  const parsed = schema.safeParse(input);
  console.log(JSON.stringify(input), parsed.success, parsed.success ? parsed.data : parsed.error.errors.map(e => ({path: e.path, message: e.message})));
}
JS

Repository: iii-hq/workers

Length of output: 5997


🌐 Web query:

zod v4 object default undefined input field default only when key is undefined

💡 Result:

In Zod v4, the behavior of .default() is designed to handle the absence of a value by short-circuiting the parsing process and returning the specified default value [1][2]. Key behaviors in Zod v4: 1. Short-circuiting: When input is undefined, .default() returns the provided value immediately, skipping the inner schema's parsing [1][3]. Consequently, the output type of a schema with .default() loses optionality (e.g., it becomes string instead of string | undefined) because the parser guarantees a non-undefined result [4][5]. 2. Defining "Only When Undefined": Because .default() treats any undefined input as an absence to be filled, it naturally applies only when the key is undefined (or absent). If you pass an explicit value, Zod validates that value against the inner schema [6]. 3. Preserving Optionality: If your goal is for the field to remain optional (retaining | undefined in the output type) while providing a default value only at runtime, .default() is generally not the correct tool as it forces a non-optional output type [4][5]. Instead, you should use .prefault(), which allows you to substitute a value into the input before parsing while preserving the schema's optional nature [1][2][3]. If you explicitly need to define a default only when a key is undefined while maintaining an optional type, consider the following pattern using .prefault() [1][3]: const schema = z.string.prefault("your-default").optional; type Schema = z.infer; // string | undefined This approach ensures that undefined inputs receive the default value during parsing, but the type signature correctly reflects that the resulting field can still be undefined [3].

Citations:


Return kind and source from directory::prompts::list/get.

PromptEntry and PromptGetOutput currently serialize only name, description, body, and modified_at, while promptRowSchema defaults missing fields to kind: 'command' and source: 'worker'. This makes system prompts appear as command in the list and forces every detail opened via getPrompt into readonly: true, hiding delete and disabling edits for user library prompts. Emit the actual kind/source in the Rust responses or store/detect it before applying these defaults.

🤖 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 `@console/web/src/lib/prompts.ts` around lines 17 - 33, Update the Rust
implementations of directory::prompts::list and directory::prompts::get,
including PromptEntry and PromptGetOutput serialization, to emit each prompt’s
actual kind and source fields. Ensure the values are populated before
promptRowSchema or promptDetailSchema defaults are applied so system prompts
retain their true kind and user library prompts retain their true source and
remain editable/deletable.

Comment thread console/web/src/pages/Prompts/index.tsx
Comment thread console/web/src/pages/Prompts/index.tsx
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.

1 participant