Remove contextMode, consolidate wake policies, and default serveUi to true

Drop the unused contextMode field from the agent schema, shared types, validators,
and all UI references. Merge wakeOnOnDemand and wakeOnAutomation into a single
wakeOnDemand toggle. Default serveUi to true and remove the onboarding prompt for it.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
Forgotten
2026-02-18 16:46:29 -06:00
parent 7ca5cfd505
commit 8c2bf0a2e6
14 changed files with 11 additions and 69 deletions

View File

@@ -34,7 +34,7 @@ function defaultConfig(): PaperclipConfig {
}, },
server: { server: {
port: 3100, port: 3100,
serveUi: false, serveUi: true,
}, },
}; };
} }

View File

@@ -20,16 +20,5 @@ export async function promptServer(): Promise<ServerConfig> {
} }
const port = Number(portStr) || 3100; const port = Number(portStr) || 3100;
return { port, serveUi: true };
const serveUi = await p.confirm({
message: "Serve the UI from the server?",
initialValue: false,
});
if (p.isCancel(serveUi)) {
p.cancel("Setup cancelled.");
process.exit(0);
}
return { port, serveUi };
} }

View File

@@ -24,7 +24,6 @@ export const agents = pgTable(
adapterType: text("adapter_type").notNull().default("process"), adapterType: text("adapter_type").notNull().default("process"),
adapterConfig: jsonb("adapter_config").$type<Record<string, unknown>>().notNull().default({}), adapterConfig: jsonb("adapter_config").$type<Record<string, unknown>>().notNull().default({}),
runtimeConfig: jsonb("runtime_config").$type<Record<string, unknown>>().notNull().default({}), runtimeConfig: jsonb("runtime_config").$type<Record<string, unknown>>().notNull().default({}),
contextMode: text("context_mode").notNull().default("thin"),
budgetMonthlyCents: integer("budget_monthly_cents").notNull().default(0), budgetMonthlyCents: integer("budget_monthly_cents").notNull().default(0),
spentMonthlyCents: integer("spent_monthly_cents").notNull().default(0), spentMonthlyCents: integer("spent_monthly_cents").notNull().default(0),
lastHeartbeatAt: timestamp("last_heartbeat_at", { withTimezone: true }), lastHeartbeatAt: timestamp("last_heartbeat_at", { withTimezone: true }),

View File

@@ -25,7 +25,7 @@ export const loggingConfigSchema = z.object({
export const serverConfigSchema = z.object({ export const serverConfigSchema = z.object({
port: z.number().int().min(1).max(65535).default(3100), port: z.number().int().min(1).max(65535).default(3100),
serveUi: z.boolean().default(false), serveUi: z.boolean().default(true),
}); });
export const paperclipConfigSchema = z.object({ export const paperclipConfigSchema = z.object({

View File

@@ -11,9 +11,6 @@ export const AGENT_STATUSES = [
] as const; ] as const;
export type AgentStatus = (typeof AGENT_STATUSES)[number]; export type AgentStatus = (typeof AGENT_STATUSES)[number];
export const AGENT_CONTEXT_MODES = ["thin", "fat"] as const;
export type AgentContextMode = (typeof AGENT_CONTEXT_MODES)[number];
export const AGENT_ADAPTER_TYPES = ["process", "http", "claude_local", "codex_local"] as const; export const AGENT_ADAPTER_TYPES = ["process", "http", "claude_local", "codex_local"] as const;
export type AgentAdapterType = (typeof AGENT_ADAPTER_TYPES)[number]; export type AgentAdapterType = (typeof AGENT_ADAPTER_TYPES)[number];

View File

@@ -1,7 +1,6 @@
export { export {
COMPANY_STATUSES, COMPANY_STATUSES,
AGENT_STATUSES, AGENT_STATUSES,
AGENT_CONTEXT_MODES,
AGENT_ADAPTER_TYPES, AGENT_ADAPTER_TYPES,
AGENT_ROLES, AGENT_ROLES,
ISSUE_STATUSES, ISSUE_STATUSES,
@@ -18,7 +17,6 @@ export {
LIVE_EVENT_TYPES, LIVE_EVENT_TYPES,
type CompanyStatus, type CompanyStatus,
type AgentStatus, type AgentStatus,
type AgentContextMode,
type AgentAdapterType, type AgentAdapterType,
type AgentRole, type AgentRole,
type IssueStatus, type IssueStatus,

View File

@@ -1,6 +1,5 @@
import type { import type {
AgentAdapterType, AgentAdapterType,
AgentContextMode,
AgentRole, AgentRole,
AgentStatus, AgentStatus,
} from "../constants.js"; } from "../constants.js";
@@ -17,7 +16,6 @@ export interface Agent {
adapterType: AgentAdapterType; adapterType: AgentAdapterType;
adapterConfig: Record<string, unknown>; adapterConfig: Record<string, unknown>;
runtimeConfig: Record<string, unknown>; runtimeConfig: Record<string, unknown>;
contextMode: AgentContextMode;
budgetMonthlyCents: number; budgetMonthlyCents: number;
spentMonthlyCents: number; spentMonthlyCents: number;
lastHeartbeatAt: Date | null; lastHeartbeatAt: Date | null;

View File

@@ -1,7 +1,6 @@
import { z } from "zod"; import { z } from "zod";
import { import {
AGENT_ADAPTER_TYPES, AGENT_ADAPTER_TYPES,
AGENT_CONTEXT_MODES,
AGENT_ROLES, AGENT_ROLES,
AGENT_STATUSES, AGENT_STATUSES,
} from "../constants.js"; } from "../constants.js";
@@ -15,7 +14,6 @@ export const createAgentSchema = z.object({
adapterType: z.enum(AGENT_ADAPTER_TYPES).optional().default("process"), adapterType: z.enum(AGENT_ADAPTER_TYPES).optional().default("process"),
adapterConfig: z.record(z.unknown()).optional().default({}), adapterConfig: z.record(z.unknown()).optional().default({}),
runtimeConfig: z.record(z.unknown()).optional().default({}), runtimeConfig: z.record(z.unknown()).optional().default({}),
contextMode: z.enum(AGENT_CONTEXT_MODES).optional().default("thin"),
budgetMonthlyCents: z.number().int().nonnegative().optional().default(0), budgetMonthlyCents: z.number().int().nonnegative().optional().default(0),
metadata: z.record(z.unknown()).optional().nullable(), metadata: z.record(z.unknown()).optional().nullable(),
}); });

View File

@@ -67,7 +67,6 @@ export function approvalService(db: Db) {
typeof payload.adapterConfig === "object" && payload.adapterConfig !== null typeof payload.adapterConfig === "object" && payload.adapterConfig !== null
? (payload.adapterConfig as Record<string, unknown>) ? (payload.adapterConfig as Record<string, unknown>)
: {}, : {},
contextMode: String(payload.contextMode ?? "thin"),
budgetMonthlyCents: budgetMonthlyCents:
typeof payload.budgetMonthlyCents === "number" ? payload.budgetMonthlyCents : 0, typeof payload.budgetMonthlyCents === "number" ? payload.budgetMonthlyCents : 0,
metadata: metadata:

View File

@@ -642,34 +642,14 @@ export function AgentConfigForm(props: AgentConfigFormProps) {
Advanced Advanced
</div> </div>
<ToggleField <ToggleField
label="Wake on assignment" label="Wake on demand"
hint={help.wakeOnAssignment} hint={help.wakeOnDemand}
checked={eff( checked={eff(
"heartbeat", "heartbeat",
"wakeOnAssignment", "wakeOnDemand",
heartbeat.wakeOnAssignment !== false, heartbeat.wakeOnDemand !== false,
)} )}
onChange={(v) => mark("heartbeat", "wakeOnAssignment", v)} onChange={(v) => mark("heartbeat", "wakeOnDemand", v)}
/>
<ToggleField
label="Wake on on-demand"
hint={help.wakeOnOnDemand}
checked={eff(
"heartbeat",
"wakeOnOnDemand",
heartbeat.wakeOnOnDemand !== false,
)}
onChange={(v) => mark("heartbeat", "wakeOnOnDemand", v)}
/>
<ToggleField
label="Wake on automation"
hint={help.wakeOnAutomation}
checked={eff(
"heartbeat",
"wakeOnAutomation",
heartbeat.wakeOnAutomation !== false,
)}
onChange={(v) => mark("heartbeat", "wakeOnAutomation", v)}
/> />
<Field label="Cooldown (sec)" hint={help.cooldownSec}> <Field label="Cooldown (sec)" hint={help.cooldownSec}>
<DraftNumberInput <DraftNumberInput
@@ -693,11 +673,6 @@ export function AgentConfigForm(props: AgentConfigFormProps) {
<div className="border-b border-border"> <div className="border-b border-border">
<div className="px-4 py-2 text-xs font-medium text-muted-foreground">Runtime</div> <div className="px-4 py-2 text-xs font-medium text-muted-foreground">Runtime</div>
<div className="px-4 pb-3 space-y-3"> <div className="px-4 pb-3 space-y-3">
<Field label="Context mode" hint={help.contextMode}>
<div className="text-sm font-mono px-2.5 py-1.5">
{props.agent.contextMode}
</div>
</Field>
<Field label="Monthly budget (cents)" hint={help.budgetMonthlyCents}> <Field label="Monthly budget (cents)" hint={help.budgetMonthlyCents}>
<DraftNumberInput <DraftNumberInput
value={eff( value={eff(

View File

@@ -42,9 +42,6 @@ export function AgentProperties({ agent, runtimeState }: AgentPropertiesProps) {
<PropertyRow label="Adapter"> <PropertyRow label="Adapter">
<span className="text-sm font-mono">{adapterLabels[agent.adapterType] ?? agent.adapterType}</span> <span className="text-sm font-mono">{adapterLabels[agent.adapterType] ?? agent.adapterType}</span>
</PropertyRow> </PropertyRow>
<PropertyRow label="Context">
<span className="text-sm">{agent.contextMode}</span>
</PropertyRow>
</div> </div>
<Separator /> <Separator />

View File

@@ -112,13 +112,10 @@ export function NewAgentDialog() {
heartbeat: { heartbeat: {
enabled: configValues.heartbeatEnabled, enabled: configValues.heartbeatEnabled,
intervalSec: configValues.intervalSec, intervalSec: configValues.intervalSec,
wakeOnAssignment: true, wakeOnDemand: true,
wakeOnOnDemand: true,
wakeOnAutomation: true,
cooldownSec: 10, cooldownSec: 10,
}, },
}, },
contextMode: "thin",
budgetMonthlyCents: 0, budgetMonthlyCents: 0,
}); });
} }

View File

@@ -152,9 +152,7 @@ export function OnboardingWizard() {
heartbeat: { heartbeat: {
enabled: true, enabled: true,
intervalSec: 300, intervalSec: 300,
wakeOnAssignment: true, wakeOnDemand: true,
wakeOnOnDemand: true,
wakeOnAutomation: true,
cooldownSec: 10, cooldownSec: 10,
}, },
}, },

View File

@@ -33,11 +33,8 @@ export const help: Record<string, string> = {
intervalSec: "Seconds between automatic heartbeat invocations.", intervalSec: "Seconds between automatic heartbeat invocations.",
timeoutSec: "Maximum seconds a run can take before being terminated. 0 means no timeout.", timeoutSec: "Maximum seconds a run can take before being terminated. 0 means no timeout.",
graceSec: "Seconds to wait after sending interrupt before force-killing the process.", graceSec: "Seconds to wait after sending interrupt before force-killing the process.",
wakeOnAssignment: "Automatically wake this agent when a new issue is assigned to it.", wakeOnDemand: "Allow this agent to be woken by assignments, API calls, UI actions, or automated systems.",
wakeOnOnDemand: "Allow this agent to be woken on demand via the API or UI.",
wakeOnAutomation: "Allow automated systems to wake this agent.",
cooldownSec: "Minimum seconds between consecutive heartbeat runs.", cooldownSec: "Minimum seconds between consecutive heartbeat runs.",
contextMode: "How context is managed between runs (thin = fresh context each run).",
budgetMonthlyCents: "Monthly spending limit in cents. 0 means no limit.", budgetMonthlyCents: "Monthly spending limit in cents. 0 means no limit.",
}; };