feat(watchdogs): add experimental self-mode watchdogs

- Add enableWatchdogEverything experimental instance setting and UI toggle.
- Add issue_watchdogs.mode and accept mode in the watchdog endpoint.
- Wake task assignees directly for stopped self-mode watchdogs.
- Add first-instruction guidance for assignment-shaped wakes.

Co-Authored-By: Paperclip <noreply@paperclip.ing>
This commit is contained in:
DottaandPaperclip committed 2026-07-29 19:07:29 +00:00
1 parent 9574cad3e8
commit 7b4722e26c
21 files changed
+554 -4

No files matched your search

+8
View File
@@ -104,6 +104,14 @@ export const INSTANCE_FEATURE_CATALOG: Record<InstanceFeatureKey, FeatureCatalog
cloudDefault: false,
selfHostedDefault: false,
},
enableWatchdogEverything: {
title: "Watchdog Everything",
description:
"On new task assignments, instruct the assignee to triage whether the task needs a completion goal and, if so, register it as a self-mode watchdog on the task itself; the watchdog then wakes the same agent directly on the task to verify the goal — no separate watchdog sub-task.",
tier: "managed",
cloudDefault: false,
selfHostedDefault: false,
},
enableIssuePlanDecompositions: {
title: "Task Plan Decomposition Panel",
description: "Show accepted-plan decomposition history on task detail pages.",
+1
View File
@@ -655,6 +655,7 @@ export type {
IssueAttachment,
IssueLabel,
IssueWatchdog,
IssueWatchdogMode,
IssueWatchdogStatus,
IssueWatchdogSummary,
} from "./issue.js";
+1
View File
@@ -53,6 +53,7 @@ export interface InstanceExperimentalSettings {
enableCases: boolean;
enableConferenceRoomChat: boolean;
enableTaskWatchdogs: boolean;
enableWatchdogEverything: boolean;
enableIssuePlanDecompositions: boolean;
enableExperimentalFileViewer: boolean;
enableCloudSync: boolean;
+9
View File
@@ -683,6 +683,14 @@ export interface IssueExecutionDecision {
export type IssueWatchdogStatus = "active" | "disabled";
// "subtask": stopped subtrees are reviewed through a separate reusable
// watchdog issue assigned to the watchdog agent (default, original behavior).
// "self": the watchdog agent is woken directly on the watched issue itself and
// reviews/verifies in place — no watchdog sub-task is created (used by the
// experimental "watchdog everything" flow where agents self-watchdog their
// own tasks).
export type IssueWatchdogMode = "subtask" | "self";
export interface IssueWatchdogSummary {
id: string;
companyId: string;
@@ -690,6 +698,7 @@ export interface IssueWatchdogSummary {
watchdogAgentId: string;
instructions: string | null;
status: IssueWatchdogStatus;
mode?: IssueWatchdogMode;
watchdogIssueId: string | null;
lastObservedFingerprint: string | null;
lastReviewedFingerprint: string | null;
@@ -130,6 +130,19 @@ describe("instance experimental settings validators", () => {
});
});
it("defaults watchdog everything off and accepts patches", () => {
const settings = instanceExperimentalSettingsSchema.parse({});
expect(settings.enableWatchdogEverything).toBe(false);
expect(
patchInstanceExperimentalSettingsSchema.parse({
enableWatchdogEverything: true,
}),
).toEqual({
enableWatchdogEverything: true,
});
});
it("accepts apps patches", () => {
expect(
patchInstanceExperimentalSettingsSchema.parse({
@@ -47,6 +47,7 @@ export const instanceExperimentalSettingsSchema = z.object({
enableCases: z.boolean().default(false),
enableConferenceRoomChat: z.boolean().default(false),
enableTaskWatchdogs: z.boolean().default(false),
enableWatchdogEverything: z.boolean().default(false),
enableIssuePlanDecompositions: z.boolean().default(false),
enableExperimentalFileViewer: z.boolean().default(false),
enableCloudSync: z.boolean().default(false),
+4
View File
@@ -497,6 +497,10 @@ export type CreateIssue = z.infer<typeof createIssueSchema>;
export const upsertIssueWatchdogSchema = z.object({
agentId: z.string().uuid(),
instructions: multilineTextSchema.optional().nullable(),
// "subtask" (default) reviews stopped subtrees through a separate watchdog
// issue; "self" wakes the watchdog agent directly on the watched issue
// (experimental "watchdog everything"). Omitted on update keeps the current mode.
mode: z.enum(["subtask", "self"]).optional(),
}).strict();
export type UpsertIssueWatchdog = z.infer<typeof upsertIssueWatchdogSchema>;