From 672eb3d2b9cd2bbad568fce7a5cbfc21984116c7 Mon Sep 17 00:00:00 2001 From: DaKheera47 Date: Sun, 15 Feb 2026 18:37:38 +0000 Subject: [PATCH] Ghostwriter always enabled --- .../components/job-chat/JobChatPanel.tsx | 31 +--------- .../src/client/pages/SettingsPage.tsx | 8 --- .../components/ChatSettingsSection.tsx | 58 ++++--------------- .../src/client/pages/settings/types.ts | 1 - .../src/server/api/routes/job-chat.test.ts | 2 +- .../src/server/repositories/settings.ts | 1 - .../src/server/services/job-chat-context.ts | 7 +-- orchestrator/src/server/services/job-chat.ts | 25 +------- .../server/services/settings-conversion.ts | 9 --- .../services/settings-update/registry.ts | 5 -- orchestrator/src/server/services/settings.ts | 11 ---- shared/src/settings-schema.ts | 1 - shared/src/testing/factories.ts | 3 - shared/src/types.ts | 3 - 14 files changed, 17 insertions(+), 148 deletions(-) diff --git a/orchestrator/src/client/components/job-chat/JobChatPanel.tsx b/orchestrator/src/client/components/job-chat/JobChatPanel.tsx index f4fa604..84ad78d 100644 --- a/orchestrator/src/client/components/job-chat/JobChatPanel.tsx +++ b/orchestrator/src/client/components/job-chat/JobChatPanel.tsx @@ -10,7 +10,6 @@ import { useCallback, useEffect, useMemo, useRef, useState } from "react"; import { toast } from "sonner"; import { Card, CardContent, CardHeader, CardTitle } from "@/components/ui/card"; import * as api from "../../api"; -import { useSettings } from "../../hooks/useSettings"; import { Composer } from "./Composer"; import { MessageList } from "./MessageList"; import { RunControls } from "./RunControls"; @@ -21,9 +20,6 @@ type JobChatPanelProps = { }; export const JobChatPanel: React.FC = ({ job }) => { - const { settings } = useSettings(); - const enabled = settings?.jobChatEnabled ?? false; - const [threads, setThreads] = useState([]); const [activeThreadId, setActiveThreadId] = useState(null); const [messages, setMessages] = useState([]); @@ -73,11 +69,6 @@ export const JobChatPanel: React.FC = ({ job }) => { }, [job.id, job.title, job.employer]); const load = useCallback(async () => { - if (!enabled) { - setIsLoading(false); - return; - } - setIsLoading(true); try { const data = await api.listJobChatThreads(job.id); @@ -96,12 +87,12 @@ export const JobChatPanel: React.FC = ({ job }) => { } } catch (error) { const message = - error instanceof Error ? error.message : "Failed to load job chat"; + error instanceof Error ? error.message : "Failed to load Ghostwriter"; toast.error(message); } finally { setIsLoading(false); } - }, [enabled, job.id, createThread, loadThreadMessages]); + }, [job.id, createThread, loadThreadMessages]); useEffect(() => { void load(); @@ -295,28 +286,12 @@ export const JobChatPanel: React.FC = ({ job }) => { onStreamEvent, ]); - if (!enabled) { - return ( - - - - - Job Copilot - - - - Enable this in Settings → Job Chat Copilot. - - - ); - } - return ( - Job Copilot + Ghostwriter diff --git a/orchestrator/src/client/pages/SettingsPage.tsx b/orchestrator/src/client/pages/SettingsPage.tsx index 8e7766f..66f80c1 100644 --- a/orchestrator/src/client/pages/SettingsPage.tsx +++ b/orchestrator/src/client/pages/SettingsPage.tsx @@ -47,7 +47,6 @@ const DEFAULT_FORM_VALUES: UpdateSettingsInput = { resumeProjects: null, rxresumeBaseResumeId: null, showSponsorInfo: null, - jobChatEnabled: null, chatStyleTone: "", chatStyleFormality: "", chatStyleConstraints: "", @@ -87,7 +86,6 @@ const NULL_SETTINGS_PAYLOAD: UpdateSettingsInput = { resumeProjects: null, rxresumeBaseResumeId: null, showSponsorInfo: null, - jobChatEnabled: null, chatStyleTone: null, chatStyleFormality: null, chatStyleConstraints: null, @@ -121,7 +119,6 @@ const mapSettingsToForm = (data: AppSettings): UpdateSettingsInput => ({ resumeProjects: data.resumeProjects, rxresumeBaseResumeId: data.rxresumeBaseResumeId ?? null, showSponsorInfo: data.overrideShowSponsorInfo, - jobChatEnabled: data.overrideJobChatEnabled, chatStyleTone: data.overrideChatStyleTone ?? "", chatStyleFormality: data.overrideChatStyleFormality ?? "", chatStyleConstraints: data.overrideChatStyleConstraints ?? "", @@ -217,10 +214,6 @@ const getDerivedSettings = (settings: AppSettings | null) => { default: settings?.defaultShowSponsorInfo ?? true, }, chat: { - enabled: { - effective: settings?.jobChatEnabled ?? false, - default: settings?.defaultJobChatEnabled ?? false, - }, tone: { effective: settings?.chatStyleTone ?? "professional", default: settings?.defaultChatStyleTone ?? "professional", @@ -590,7 +583,6 @@ export const SettingsPage: React.FC = () => { resumeProjects: resumeProjectsOverride, rxresumeBaseResumeId: normalizeString(data.rxresumeBaseResumeId), showSponsorInfo: nullIfSame(data.showSponsorInfo, display.default), - jobChatEnabled: nullIfSame(data.jobChatEnabled, chat.enabled.default), chatStyleTone: normalizeString(data.chatStyleTone), chatStyleFormality: normalizeString(data.chatStyleFormality), chatStyleConstraints: normalizeString(data.chatStyleConstraints), diff --git a/orchestrator/src/client/pages/settings/components/ChatSettingsSection.tsx b/orchestrator/src/client/pages/settings/components/ChatSettingsSection.tsx index 092e64c..8511431 100644 --- a/orchestrator/src/client/pages/settings/components/ChatSettingsSection.tsx +++ b/orchestrator/src/client/pages/settings/components/ChatSettingsSection.tsx @@ -8,7 +8,6 @@ import { AccordionItem, AccordionTrigger, } from "@/components/ui/accordion"; -import { Checkbox } from "@/components/ui/checkbox"; import { Select, SelectContent, @@ -29,48 +28,20 @@ export const ChatSettingsSection: React.FC = ({ isLoading, isSaving, }) => { - const { enabled, tone, formality, constraints, doNotUse } = values; + const { tone, formality, constraints, doNotUse } = values; - const { control, register, watch } = useFormContext(); - const isEnabled = watch("jobChatEnabled") ?? enabled.default; + const { control, register } = useFormContext(); return ( - Job Chat Copilot + Ghostwriter
-
- ( - { - field.onChange( - checked === "indeterminate" ? null : checked === true, - ); - }} - disabled={isLoading || isSaving} - /> - )} - /> -
- -

- Adds a persistent chat thread on each job page with prefilled - job and profile context. -

-
-
+

+ Ghostwriter is always on. Configure only writing style here. +

@@ -86,7 +57,7 @@ export const ChatSettingsSection: React.FC = ({ field.onChange(value)} - disabled={isLoading || isSaving || !isEnabled} + disabled={isLoading || isSaving} > @@ -136,8 +107,8 @@ export const ChatSettingsSection: React.FC = ({ label="Constraints" inputProps={register("chatStyleConstraints")} placeholder="Example: keep answers under 120 words and include bullet points" - disabled={isLoading || isSaving || !isEnabled} - helper="Optional global writing constraints used by job chat replies." + disabled={isLoading || isSaving} + helper="Optional global writing constraints used by Ghostwriter replies." current={constraints.effective || "—"} /> @@ -145,7 +116,7 @@ export const ChatSettingsSection: React.FC = ({ label="Do-not-use terms" inputProps={register("chatStyleDoNotUse")} placeholder="Example: synergize, leverage" - disabled={isLoading || isSaving || !isEnabled} + disabled={isLoading || isSaving} helper="Optional comma-separated words or phrases to avoid." current={doNotUse.effective || "—"} /> @@ -153,13 +124,6 @@ export const ChatSettingsSection: React.FC = ({
-
-
Enabled
-
- Effective: {enabled.effective ? "Yes" : "No"} | Default:{" "} - {enabled.default ? "Yes" : "No"} -
-
Tone
diff --git a/orchestrator/src/client/pages/settings/types.ts b/orchestrator/src/client/pages/settings/types.ts index 4955871..cc2af77 100644 --- a/orchestrator/src/client/pages/settings/types.ts +++ b/orchestrator/src/client/pages/settings/types.ts @@ -15,7 +15,6 @@ export type ModelValues = EffectiveDefault & { export type WebhookValues = EffectiveDefault; export type DisplayValues = EffectiveDefault; export type ChatValues = { - enabled: EffectiveDefault; tone: EffectiveDefault; formality: EffectiveDefault; constraints: EffectiveDefault; diff --git a/orchestrator/src/server/api/routes/job-chat.test.ts b/orchestrator/src/server/api/routes/job-chat.test.ts index 46a3bc8..8b844b7 100644 --- a/orchestrator/src/server/api/routes/job-chat.test.ts +++ b/orchestrator/src/server/api/routes/job-chat.test.ts @@ -90,7 +90,7 @@ vi.mock("../../services/job-chat", () => ({ })), })); -describe.sequential("Job Chat API", () => { +describe.sequential("Ghostwriter API", () => { let server: Server; let baseUrl: string; let closeDb: () => void; diff --git a/orchestrator/src/server/repositories/settings.ts b/orchestrator/src/server/repositories/settings.ts index aacb67b..40c2774 100644 --- a/orchestrator/src/server/repositories/settings.ts +++ b/orchestrator/src/server/repositories/settings.ts @@ -26,7 +26,6 @@ export type SettingKey = | "jobspyResultsWanted" | "jobspyCountryIndeed" | "showSponsorInfo" - | "jobChatEnabled" | "chatStyleTone" | "chatStyleFormality" | "chatStyleConstraints" diff --git a/orchestrator/src/server/services/job-chat-context.ts b/orchestrator/src/server/services/job-chat-context.ts index f28616e..31df771 100644 --- a/orchestrator/src/server/services/job-chat-context.ts +++ b/orchestrator/src/server/services/job-chat-context.ts @@ -106,7 +106,7 @@ function buildProfileSnapshot(profile: ResumeProfile): string { function buildSystemPrompt(style: JobChatStyle): string { return compactJoin([ - "You are a job application copilot for a single job.", + "You are Ghostwriter, a job-application writing assistant for a single job.", "Use only the provided job and profile context unless the user gives extra details.", "Do not claim actions were executed. You are read-only and advisory.", "If details are missing, say what is missing before making assumptions.", @@ -145,11 +145,6 @@ async function resolveStyle(): Promise { }; } -export async function isJobChatEnabled(): Promise { - const overrides = await settingsRepo.getAllSettings(); - return resolveSettingValue("jobChatEnabled", overrides.jobChatEnabled).value; -} - export async function buildJobChatPromptContext( jobId: string, ): Promise { diff --git a/orchestrator/src/server/services/job-chat.ts b/orchestrator/src/server/services/job-chat.ts index 12ed81c..1d946a0 100644 --- a/orchestrator/src/server/services/job-chat.ts +++ b/orchestrator/src/server/services/job-chat.ts @@ -9,10 +9,7 @@ import { } from "../infra/errors"; import * as jobChatRepo from "../repositories/job-chat"; import * as settingsRepo from "../repositories/settings"; -import { - buildJobChatPromptContext, - isJobChatEnabled, -} from "./job-chat-context"; +import { buildJobChatPromptContext } from "./job-chat-context"; import { LlmService } from "./llm/service"; import type { JsonSchemaDefinition } from "./llm/types"; @@ -98,15 +95,6 @@ async function buildConversationMessages( })); } -async function ensureChatEnabled(): Promise { - const enabled = await isJobChatEnabled(); - if (!enabled) { - throw badRequest( - "Job chat is disabled. Enable it in Settings > Job Chat Copilot.", - ); - } -} - type GenerateReplyOptions = { jobId: string; threadId: string; @@ -145,12 +133,10 @@ export async function createThread(input: { jobId: string; title?: string | null; }) { - await ensureChatEnabled(); return jobChatRepo.createThread(input); } export async function listThreads(jobId: string) { - await ensureChatEnabled(); return jobChatRepo.listThreadsForJob(jobId); } @@ -160,7 +146,6 @@ export async function listMessages(input: { limit?: number; offset?: number; }) { - await ensureChatEnabled(); const thread = await jobChatRepo.getThreadForJob(input.jobId, input.threadId); if (!thread) { throw notFound("Thread not found for this job"); @@ -175,8 +160,6 @@ export async function listMessages(input: { async function runAssistantReply( options: GenerateReplyOptions, ): Promise<{ runId: string; messageId: string; message: string }> { - await ensureChatEnabled(); - const thread = await jobChatRepo.getThreadForJob( options.jobId, options.threadId, @@ -382,8 +365,6 @@ export async function sendMessage(input: { content: string; stream?: GenerateReplyOptions["stream"]; }) { - await ensureChatEnabled(); - const content = input.content.trim(); if (!content) { throw badRequest("Message content is required"); @@ -425,8 +406,6 @@ export async function regenerateMessage(input: { assistantMessageId: string; stream?: GenerateReplyOptions["stream"]; }) { - await ensureChatEnabled(); - const thread = await jobChatRepo.getThreadForJob(input.jobId, input.threadId); if (!thread) { throw notFound("Thread not found for this job"); @@ -489,8 +468,6 @@ export async function cancelRun(input: { threadId: string; runId: string; }): Promise<{ cancelled: boolean; alreadyFinished: boolean }> { - await ensureChatEnabled(); - const run = await jobChatRepo.getRunById(input.runId); if (!run || run.threadId !== input.threadId || run.jobId !== input.jobId) { throw notFound("Run not found for this thread"); diff --git a/orchestrator/src/server/services/settings-conversion.ts b/orchestrator/src/server/services/settings-conversion.ts index cee5cda..b8df62a 100644 --- a/orchestrator/src/server/services/settings-conversion.ts +++ b/orchestrator/src/server/services/settings-conversion.ts @@ -13,7 +13,6 @@ type SettingsConversionValueMap = { jobspyResultsWanted: number; jobspyCountryIndeed: string; showSponsorInfo: boolean; - jobChatEnabled: boolean; chatStyleTone: string; chatStyleFormality: string; chatStyleConstraints: string; @@ -142,14 +141,6 @@ export const settingsConversionMetadata: SettingsConversionMetadata = { serialize: serializeBitBool, resolve: resolveWithNullishFallback, }, - jobChatEnabled: { - defaultValue: () => - (process.env.JOB_CHAT_ENABLED || "").toLowerCase() === "true" || - process.env.JOB_CHAT_ENABLED === "1", - parseOverride: parseBitBoolOrNull, - serialize: serializeBitBool, - resolve: resolveWithNullishFallback, - }, chatStyleTone: { defaultValue: () => process.env.CHAT_STYLE_TONE || "professional", parseOverride: (raw) => raw ?? null, diff --git a/orchestrator/src/server/services/settings-update/registry.ts b/orchestrator/src/server/services/settings-update/registry.ts index 93c21d0..1d0d630 100644 --- a/orchestrator/src/server/services/settings-update/registry.ts +++ b/orchestrator/src/server/services/settings-update/registry.ts @@ -188,11 +188,6 @@ export const settingsUpdateRegistry: Partial<{ actions: [metadataPersistAction("showSponsorInfo", value)], }), ), - jobChatEnabled: singleAction(({ value }) => - result({ - actions: [metadataPersistAction("jobChatEnabled", value)], - }), - ), chatStyleTone: singleAction(({ value }) => result({ actions: [metadataPersistAction("chatStyleTone", value)], diff --git a/orchestrator/src/server/services/settings.ts b/orchestrator/src/server/services/settings.ts index 9778f18..71425af 100644 --- a/orchestrator/src/server/services/settings.ts +++ b/orchestrator/src/server/services/settings.ts @@ -146,14 +146,6 @@ export async function getEffectiveSettings(): Promise { const overrideShowSponsorInfo = showSponsorInfoSetting.overrideValue; const showSponsorInfo = showSponsorInfoSetting.value; - const jobChatEnabledSetting = resolveSettingValue( - "jobChatEnabled", - overrides.jobChatEnabled, - ); - const defaultJobChatEnabled = jobChatEnabledSetting.defaultValue; - const overrideJobChatEnabled = jobChatEnabledSetting.overrideValue; - const jobChatEnabled = jobChatEnabledSetting.value; - const chatStyleToneSetting = resolveSettingValue( "chatStyleTone", overrides.chatStyleTone, @@ -286,9 +278,6 @@ export async function getEffectiveSettings(): Promise { showSponsorInfo, defaultShowSponsorInfo, overrideShowSponsorInfo, - jobChatEnabled, - defaultJobChatEnabled, - overrideJobChatEnabled, chatStyleTone, defaultChatStyleTone, overrideChatStyleTone, diff --git a/shared/src/settings-schema.ts b/shared/src/settings-schema.ts index c6352f2..07f4280 100644 --- a/shared/src/settings-schema.ts +++ b/shared/src/settings-schema.ts @@ -54,7 +54,6 @@ export const updateSettingsSchema = z .optional(), jobspyCountryIndeed: z.string().trim().max(100).nullable().optional(), showSponsorInfo: z.boolean().nullable().optional(), - jobChatEnabled: z.boolean().nullable().optional(), chatStyleTone: z.string().trim().max(100).nullable().optional(), chatStyleFormality: z.string().trim().max(100).nullable().optional(), chatStyleConstraints: z.string().trim().max(4000).nullable().optional(), diff --git a/shared/src/testing/factories.ts b/shared/src/testing/factories.ts index f5ab03c..2b2a087 100644 --- a/shared/src/testing/factories.ts +++ b/shared/src/testing/factories.ts @@ -179,9 +179,6 @@ export const createAppSettings = ( showSponsorInfo: true, defaultShowSponsorInfo: true, overrideShowSponsorInfo: null, - jobChatEnabled: false, - defaultJobChatEnabled: false, - overrideJobChatEnabled: null, chatStyleTone: "professional", defaultChatStyleTone: "professional", overrideChatStyleTone: null, diff --git a/shared/src/types.ts b/shared/src/types.ts index 512eeda..f220f74 100644 --- a/shared/src/types.ts +++ b/shared/src/types.ts @@ -913,9 +913,6 @@ export interface AppSettings { showSponsorInfo: boolean; defaultShowSponsorInfo: boolean; overrideShowSponsorInfo: boolean | null; - jobChatEnabled: boolean; - defaultJobChatEnabled: boolean; - overrideJobChatEnabled: boolean | null; chatStyleTone: string; defaultChatStyleTone: string; overrideChatStyleTone: string | null;