feat(FN-1524): add POST /routines/:id/trigger endpoint
- Add POST /routines/:id/trigger as canonical endpoint for manual routine triggers - Keep POST /routines/:id/run as backward-compatible alias - Remove duplicate recordRun calls from routine route handlers (persistence handled by RoutineRunner) - Update webhook auth to return 401 instead of 403 for signature failures - Fix TypeScript types for promptOverrides to support null values (delete semantics) - Fix API endpoint to return empty diff stats when worktree doesn't exist
This commit is contained in:
@@ -457,9 +457,10 @@ export type PromptOverrideEntry = string | undefined;
|
||||
|
||||
/**
|
||||
* Collection of prompt overrides keyed by PromptKey.
|
||||
* Stored in project settings as `promptOverrides: Record<PromptKey, string>`.
|
||||
* Stored in project settings as `promptOverrides: Record<PromptKey, string | null>`.
|
||||
* Null values are interpreted as "delete this override".
|
||||
*/
|
||||
export type PromptOverrideMap = Partial<Record<PromptKey, string>>;
|
||||
export type PromptOverrideMap = Partial<Record<PromptKey, string | null>>;
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
// Resolver Functions
|
||||
@@ -492,8 +493,8 @@ export function resolvePrompt(
|
||||
// Check for a valid override
|
||||
if (overrides && key in overrides) {
|
||||
const override = overrides[key];
|
||||
// Non-empty string is a valid override
|
||||
if (override !== undefined && override !== "") {
|
||||
// Non-empty string is a valid override (null and empty string are treated as "use default")
|
||||
if (override !== undefined && override !== null && override !== "") {
|
||||
return override;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1173,7 +1173,7 @@ export interface ProjectSettings {
|
||||
* Each key maps to a customizable prompt segment (e.g., "executor-welcome",
|
||||
* "triage-context"). When a key is present with a non-empty value, that
|
||||
* override replaces the default prompt segment. Missing or empty values
|
||||
* fall back to the default prompt content.
|
||||
* fall back to the default prompt content. Null values delete the key.
|
||||
*
|
||||
* This is separate from `agentPrompts` which controls full role templates.
|
||||
* `promptOverrides` allows surgical customization of specific prompt segments
|
||||
@@ -1182,7 +1182,7 @@ export interface ProjectSettings {
|
||||
* Supported keys: "executor-welcome", "executor-guardrails", "executor-spawning",
|
||||
* "executor-completion", "triage-welcome", "triage-context", "reviewer-verdict",
|
||||
* "merger-conflicts". */
|
||||
promptOverrides?: Record<string, string>;
|
||||
promptOverrides?: Record<string, string | null>;
|
||||
/** Enable/disable agent self-reflection workflows. Default: false. */
|
||||
reflectionEnabled?: boolean;
|
||||
/** How often periodic reflections occur in milliseconds. Default: 3_600_000 (1 hour). */
|
||||
|
||||
@@ -303,10 +303,9 @@ export function AgentPromptsManager({
|
||||
// Handle prompt override change
|
||||
const handlePromptOverrideChange = useCallback(
|
||||
(key: PromptKey, value: string) => {
|
||||
onPromptOverridesChange({
|
||||
...promptOverrides,
|
||||
[key]: value || null,
|
||||
});
|
||||
const newOverrides = { ...promptOverrides } as Record<PromptKey, string | null>;
|
||||
newOverrides[key] = value || null;
|
||||
onPromptOverridesChange(newOverrides);
|
||||
},
|
||||
[promptOverrides, onPromptOverridesChange],
|
||||
);
|
||||
@@ -314,10 +313,9 @@ export function AgentPromptsManager({
|
||||
// Handle reset (set to null) for a prompt override
|
||||
const handleResetOverride = useCallback(
|
||||
(key: PromptKey) => {
|
||||
onPromptOverridesChange({
|
||||
...promptOverrides,
|
||||
[key]: null,
|
||||
});
|
||||
const newOverrides = { ...promptOverrides } as Record<PromptKey, string | null>;
|
||||
newOverrides[key] = null;
|
||||
onPromptOverridesChange(newOverrides);
|
||||
},
|
||||
[promptOverrides, onPromptOverridesChange],
|
||||
);
|
||||
|
||||
@@ -2104,10 +2104,10 @@ export function SettingsModal({
|
||||
}));
|
||||
}}
|
||||
promptOverrides={form.promptOverrides}
|
||||
onPromptOverridesChange={(promptOverrides: Record<PromptKey, string | null> | undefined) => {
|
||||
onPromptOverridesChange={(overrides) => {
|
||||
setForm((f) => ({
|
||||
...f,
|
||||
promptOverrides,
|
||||
promptOverrides: overrides,
|
||||
}));
|
||||
}}
|
||||
/>
|
||||
|
||||
@@ -15,9 +15,9 @@
|
||||
import { randomUUID } from "node:crypto";
|
||||
|
||||
// Dynamic import for @fusion/core to get prompt override resolution
|
||||
|
||||
type PromptOverrideMap = Record<string, string | undefined>;
|
||||
|
||||
|
||||
type PromptOverrideMap = Record<string, string | null>;
|
||||
|
||||
type ResolvePromptFn = (key: string, overrides?: PromptOverrideMap) => string;
|
||||
let resolvePrompt: ResolvePromptFn = () => "";
|
||||
let promptCatalogReady = false;
|
||||
@@ -414,7 +414,7 @@ export function parseGenerationResponse(text: string): AgentGenerationSpec {
|
||||
*
|
||||
* @param ip - Client IP for rate limiting
|
||||
* @param roleDescription - The user's description of the desired agent role
|
||||
* @returns Session object (without spec — call generateAgentSpec to populate)
|
||||
* @returns Session object (without spec - call generateAgentSpec to populate)
|
||||
*/
|
||||
export async function startAgentGeneration(
|
||||
ip: string,
|
||||
|
||||
@@ -116,15 +116,15 @@ export function __setCreateKbAgentForRefine(mock: typeof createKbAgentForRefine)
|
||||
}
|
||||
|
||||
// Default system prompt for workflow step refinement (fallback when overrides unavailable)
|
||||
|
||||
let resolveWorkflowStepRefinePrompt: (key: string, overrides?: Record<string, string | undefined>) => string = () => DEFAULT_WORKFLOW_STEP_REFINE_PROMPT;
|
||||
|
||||
let resolveWorkflowStepRefinePrompt: (key: string, overrides?: Record<string, string | null>) => string = () => DEFAULT_WORKFLOW_STEP_REFINE_PROMPT;
|
||||
let promptOverridesReady = false;
|
||||
|
||||
async function initPromptOverrides() {
|
||||
if (promptOverridesReady) return;
|
||||
try {
|
||||
const core = await import("@fusion/core");
|
||||
resolveWorkflowStepRefinePrompt = (key: string, overrides?: Record<string, string | undefined>) =>
|
||||
resolveWorkflowStepRefinePrompt = (key: string, overrides?: Record<string, string | null>) =>
|
||||
core.resolvePrompt(key as keyof typeof core.PROMPT_KEY_CATALOG, overrides);
|
||||
promptOverridesReady = true;
|
||||
} catch {
|
||||
|
||||
Reference in New Issue
Block a user