@selesai/code 0.13.12 → 0.13.14
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +9 -0
- package/dist/extensions/pi-subagents/CHANGELOG.md +77 -1
- package/dist/extensions/pi-subagents/VISION.md +12 -1
- package/dist/extensions/pi-subagents/docs/agents.md +13 -6
- package/dist/extensions/pi-subagents/docs/configuration.md +35 -9
- package/dist/extensions/pi-subagents/docs/extension-api.md +1 -1
- package/dist/extensions/pi-subagents/docs/missions.md +1 -1
- package/dist/extensions/pi-subagents/docs/models.md +6 -6
- package/dist/extensions/pi-subagents/docs/observability.md +6 -3
- package/dist/extensions/pi-subagents/docs/tool-reference.md +3 -3
- package/dist/extensions/pi-subagents/docs/watchdog.md +92 -114
- package/dist/extensions/pi-subagents/docs/workflows.md +1 -1
- package/dist/extensions/pi-subagents/install.mjs +1 -2
- package/dist/extensions/pi-subagents/package.json +1 -1
- package/dist/extensions/pi-subagents/skills/pi-subagents/references/constraints-and-recipes.md +1 -1
- package/dist/extensions/pi-subagents/skills/pi-subagents/references/execution-controls.md +6 -6
- package/dist/extensions/pi-subagents/skills/pi-subagents/references/management-authoring-rpc.md +2 -2
- package/dist/extensions/pi-subagents/skills/pi-subagents/references/multi-lane-orchestration.md +1 -1
- package/dist/extensions/pi-subagents/skills/pi-subagents/references/prompting-and-roles.md +4 -4
- package/dist/extensions/pi-subagents/skills/pi-subagents/references/review-and-validation.md +1 -1
- package/dist/extensions/pi-subagents/src/agents/agent-management.ts +41 -4
- package/dist/extensions/pi-subagents/src/agents/agent-serializer.ts +3 -0
- package/dist/extensions/pi-subagents/src/agents/agents.ts +120 -124
- package/dist/extensions/pi-subagents/src/agents/runtime-agent-registry.ts +5 -1
- package/dist/extensions/pi-subagents/src/api/preflight.ts +4 -0
- package/dist/extensions/pi-subagents/src/api/shared-types.ts +3 -0
- package/dist/extensions/pi-subagents/src/extension/config.ts +20 -0
- package/dist/extensions/pi-subagents/src/extension/public-execution.ts +1 -0
- package/dist/extensions/pi-subagents/src/extension/schemas.ts +6 -2
- package/dist/extensions/pi-subagents/src/extension/tool-description.ts +1 -1
- package/dist/extensions/pi-subagents/src/inspectors/herdr/inspector-runner.ts +19 -13
- package/dist/extensions/pi-subagents/src/runs/background/active-async-capacity.ts +26 -8
- package/dist/extensions/pi-subagents/src/runs/background/async-execution.ts +103 -23
- package/dist/extensions/pi-subagents/src/runs/background/async-resume.ts +6 -2
- package/dist/extensions/pi-subagents/src/runs/background/async-status.ts +18 -2
- package/dist/extensions/pi-subagents/src/runs/background/notify.ts +54 -3
- package/dist/extensions/pi-subagents/src/runs/background/process-terminal.ts +16 -0
- package/dist/extensions/pi-subagents/src/runs/background/run-status.ts +22 -2
- package/dist/extensions/pi-subagents/src/runs/background/scheduled-runs.ts +63 -6
- package/dist/extensions/pi-subagents/src/runs/background/steering.ts +4 -1
- package/dist/extensions/pi-subagents/src/runs/background/subagent-runner.ts +69 -12
- package/dist/extensions/pi-subagents/src/runs/background/wait-completions.ts +13 -0
- package/dist/extensions/pi-subagents/src/runs/background/wait-tool.ts +3 -1
- package/dist/extensions/pi-subagents/src/runs/foreground/execution.ts +21 -8
- package/dist/extensions/pi-subagents/src/runs/foreground/subagent-executor.ts +59 -6
- package/dist/extensions/pi-subagents/src/runs/shared/acceptance.ts +95 -18
- package/dist/extensions/pi-subagents/src/runs/shared/async-status-projection.ts +11 -43
- package/dist/extensions/pi-subagents/src/runs/shared/capability-ceiling.ts +1 -0
- package/dist/extensions/pi-subagents/src/runs/shared/dynamic-fanout.ts +1 -1
- package/dist/extensions/pi-subagents/src/runs/shared/lane-metadata.ts +24 -3
- package/dist/extensions/pi-subagents/src/runs/shared/parallel-handoff.ts +4 -0
- package/dist/extensions/pi-subagents/src/runs/shared/parallel-utils.ts +2 -6
- package/dist/extensions/pi-subagents/src/runs/shared/permissions.ts +1 -0
- package/dist/extensions/pi-subagents/src/runs/shared/pi-args.ts +33 -18
- package/dist/extensions/pi-subagents/src/runs/shared/pi-spawn.ts +73 -37
- package/dist/extensions/pi-subagents/src/runs/shared/structured-output.ts +33 -6
- package/dist/extensions/pi-subagents/src/runs/shared/subagent-control.ts +4 -2
- package/dist/extensions/pi-subagents/src/runs/shared/subagent-prompt-runtime.ts +20 -3
- package/dist/extensions/pi-subagents/src/runs/shared/task-intent.ts +21 -7
- package/dist/extensions/pi-subagents/src/runs/shared/tool-timeout.ts +1 -1
- package/dist/extensions/pi-subagents/src/runs/shared/worktree.ts +467 -63
- package/dist/extensions/pi-subagents/src/shared/atomic-json.ts +3 -1
- package/dist/extensions/pi-subagents/src/shared/fork-context.ts +0 -12
- package/dist/extensions/pi-subagents/src/shared/fork-session-cwd.ts +27 -0
- package/dist/extensions/pi-subagents/src/shared/launch-contract.ts +3 -0
- package/dist/extensions/pi-subagents/src/shared/types.ts +42 -4
- package/dist/extensions/pi-subagents/src/shared/utils.ts +18 -7
- package/dist/extensions/pi-subagents/src/slash/slash-commands.ts +1 -1
- package/dist/extensions/pi-subagents/src/slash/subagents-admin.ts +26 -12
- package/dist/extensions/pi-subagents/src/tui/fleet-status.ts +61 -2
- package/dist/extensions/pi-subagents/src/tui/fleet.ts +12 -7
- package/dist/extensions/pi-subagents/src/tui/render.ts +227 -14
- package/dist/extensions/pi-subagents/src/watchdog/child-status.ts +56 -34
- package/dist/extensions/pi-subagents/src/watchdog/diff-tool.ts +77 -0
- package/dist/extensions/pi-subagents/src/watchdog/emission-guard.ts +5 -3
- package/dist/extensions/pi-subagents/src/watchdog/guidance.ts +20 -0
- package/dist/extensions/pi-subagents/src/watchdog/register-child.ts +16 -13
- package/dist/extensions/pi-subagents/src/watchdog/register-main.ts +10 -9
- package/dist/extensions/pi-subagents/src/watchdog/render.ts +4 -5
- package/dist/extensions/pi-subagents/src/watchdog/review.ts +15 -4
- package/dist/extensions/pi-subagents/src/watchdog/rules.ts +70 -0
- package/dist/extensions/pi-subagents/src/watchdog/runtime.ts +75 -92
- package/dist/extensions/pi-subagents/src/watchdog/scope.ts +2 -1
- package/dist/extensions/pi-subagents/src/watchdog/settings.ts +48 -104
- package/dist/extensions/pi-subagents/src/watchdog/types.ts +18 -32
- package/dist/extensions/pi-subagents/src/watchdog/warning-format.ts +0 -1
- package/dist/extensions/pi-subagents/src/workflows/chat-progress.ts +3 -2
- package/dist/extensions/pi-subagents/src/workflows/workflow-checklist.ts +439 -0
- package/dist/extensions/pi-subagents/src/workflows/workflow-preflight.ts +28 -1
- package/dist/extensions/pi-subagents/test/integration/async-execution.test.ts +335 -21
- package/dist/extensions/pi-subagents/test/integration/async-job-tracker.test.ts +4 -1
- package/dist/extensions/pi-subagents/test/integration/async-status.test.ts +1 -1
- package/dist/extensions/pi-subagents/test/integration/fork-context-execution.test.ts +22 -18
- package/dist/extensions/pi-subagents/test/integration/intercom-result-delivery.test.ts +0 -1
- package/dist/extensions/pi-subagents/test/integration/render-widget.test.ts +21 -18
- package/dist/extensions/pi-subagents/test/integration/single-execution.test.ts +307 -22
- package/dist/extensions/pi-subagents/test/integration/slash-commands.test.ts +1 -0
- package/dist/extensions/pi-subagents/test/support/helpers.ts +25 -0
- package/dist/extensions/pi-subagents/test/support/isolated-temp-root.mjs +15 -4
- package/dist/extensions/pi-subagents/test/support/mock-pi-script.mjs +17 -2
- package/dist/extensions/pi-subagents/test/unit/acceptance.test.ts +96 -15
- package/dist/extensions/pi-subagents/test/unit/active-async-capacity.test.ts +53 -5
- package/dist/extensions/pi-subagents/test/unit/agent-frontmatter.test.ts +20 -0
- package/dist/extensions/pi-subagents/test/unit/agent-management.test.ts +221 -8
- package/dist/extensions/pi-subagents/test/unit/agent-overrides.test.ts +45 -24
- package/dist/extensions/pi-subagents/test/unit/agent-scan-dirs.test.ts +133 -0
- package/dist/extensions/pi-subagents/test/unit/async-execution.test.ts +28 -2
- package/dist/extensions/pi-subagents/test/unit/async-interrupt-action.test.ts +1 -1
- package/dist/extensions/pi-subagents/test/unit/async-resume.test.ts +2 -0
- package/dist/extensions/pi-subagents/test/unit/async-retention.test.ts +4 -1
- package/dist/extensions/pi-subagents/test/unit/async-status-projection.test.ts +18 -4
- package/dist/extensions/pi-subagents/test/unit/atomic-json.test.ts +16 -0
- package/dist/extensions/pi-subagents/test/unit/completion-guard.test.ts +38 -0
- package/dist/extensions/pi-subagents/test/unit/default-extensions.test.ts +2 -2
- package/dist/extensions/pi-subagents/test/unit/fleet-status.test.ts +21 -0
- package/dist/extensions/pi-subagents/test/unit/fleet.test.ts +19 -6
- package/dist/extensions/pi-subagents/test/unit/get-final-output.test.ts +26 -0
- package/dist/extensions/pi-subagents/test/unit/herdr-inspector.test.ts +22 -1
- package/dist/extensions/pi-subagents/test/unit/index-child-registration.test.ts +2 -6
- package/dist/extensions/pi-subagents/test/unit/notify.test.ts +66 -1
- package/dist/extensions/pi-subagents/test/unit/pi-args-permission-system.test.ts +1 -1
- package/dist/extensions/pi-subagents/test/unit/pi-args.test.ts +116 -10
- package/dist/extensions/pi-subagents/test/unit/pi-spawn.test.ts +136 -18
- package/dist/extensions/pi-subagents/test/unit/preflight.test.ts +20 -0
- package/dist/extensions/pi-subagents/test/unit/process-terminal.test.ts +22 -0
- package/dist/extensions/pi-subagents/test/unit/profiles.test.ts +1 -0
- package/dist/extensions/pi-subagents/test/unit/project-panes-public-api.test.ts +8 -0
- package/dist/extensions/pi-subagents/test/unit/render-helpers.test.ts +127 -48
- package/dist/extensions/pi-subagents/test/unit/run-status.test.ts +68 -0
- package/dist/extensions/pi-subagents/test/unit/runtime-agent-registration.test.ts +17 -0
- package/dist/extensions/pi-subagents/test/unit/scheduled-runs.test.ts +88 -0
- package/dist/extensions/pi-subagents/test/unit/schemas.test.ts +4 -2
- package/dist/extensions/pi-subagents/test/unit/scripted-workflow.test.ts +9 -9
- package/dist/extensions/pi-subagents/test/unit/steering-action.test.ts +2 -2
- package/dist/extensions/pi-subagents/test/unit/steering.test.ts +9 -3
- package/dist/extensions/pi-subagents/test/unit/subagent-control.test.ts +22 -9
- package/dist/extensions/pi-subagents/test/unit/subagent-prompt-runtime.test.ts +91 -8
- package/dist/extensions/pi-subagents/test/unit/task-intent.test.ts +27 -0
- package/dist/extensions/pi-subagents/test/unit/temp-paths.test.ts +50 -0
- package/dist/extensions/pi-subagents/test/unit/tool-description.test.ts +0 -1
- package/dist/extensions/pi-subagents/test/unit/tool-timeout.test.ts +2 -2
- package/dist/extensions/pi-subagents/test/unit/wait-completions.test.ts +34 -0
- package/dist/extensions/pi-subagents/test/unit/wait-subscriptions.test.ts +1 -2
- package/dist/extensions/pi-subagents/test/unit/watchdog-child-status.test.ts +73 -13
- package/dist/extensions/pi-subagents/test/unit/watchdog-diff-tool.test.ts +79 -0
- package/dist/extensions/pi-subagents/test/unit/watchdog-permission-arbiter.test.ts +2 -4
- package/dist/extensions/pi-subagents/test/unit/watchdog-render.test.ts +7 -8
- package/dist/extensions/pi-subagents/test/unit/watchdog-review.test.ts +48 -3
- package/dist/extensions/pi-subagents/test/unit/watchdog-rules.test.ts +37 -0
- package/dist/extensions/pi-subagents/test/unit/watchdog-runtime.test.ts +36 -118
- package/dist/extensions/pi-subagents/test/unit/watchdog-scope.test.ts +1 -1
- package/dist/extensions/pi-subagents/test/unit/watchdog-settings.test.ts +37 -14
- package/dist/extensions/pi-subagents/test/unit/workflow-chat-progress.test.ts +45 -26
- package/dist/extensions/pi-subagents/test/unit/workflow-checklist.test.ts +223 -0
- package/dist/extensions/pi-subagents/test/unit/workflow-launch-params.test.ts +50 -1
- package/dist/extensions/pi-subagents/test/unit/workflow-preflight.test.ts +22 -0
- package/dist/extensions/pi-subagents/test/unit/worktree.test.ts +136 -4
- package/dist/extensions/pi-zentui/README.md +19 -5
- package/dist/extensions/pi-zentui/docs/configuration.md +37 -9
- package/dist/extensions/pi-zentui/extensions/zentui/config.ts +29 -0
- package/dist/extensions/pi-zentui/extensions/zentui/index.ts +31 -2
- package/dist/extensions/pi-zentui/extensions/zentui/prototype-patch-registry.ts +55 -11
- package/dist/extensions/pi-zentui/extensions/zentui/settings-command.ts +115 -3
- package/dist/extensions/pi-zentui/extensions/zentui/settings-previews.ts +119 -2
- package/dist/extensions/pi-zentui/extensions/zentui/thinking-experimental.ts +1768 -0
- package/dist/extensions/pi-zentui/extensions/zentui/thinking-status.ts +69 -0
- package/dist/extensions/pi-zentui/extensions/zentui/thinking-steps.ts +155 -0
- package/dist/extensions/pi-zentui/extensions/zentui/ui.ts +2 -1
- package/dist/extensions/pi-zentui/extensions/zentui/user-message-osc.ts +16 -2
- package/dist/extensions/pi-zentui/extensions/zentui/user-message-styles.ts +1 -1
- package/dist/extensions/pi-zentui/test/config-load-lifecycle.test.ts +124 -0
- package/dist/extensions/pi-zentui/test/config.test.ts +82 -2
- package/dist/extensions/pi-zentui/test/editor-viewport-indicators.test.ts +19 -4
- package/dist/extensions/pi-zentui/test/extension-compliance.test.ts +58 -9
- package/dist/extensions/pi-zentui/test/package-contents.mjs +33 -0
- package/dist/extensions/pi-zentui/test/prototype-patch-registry.test.ts +131 -0
- package/dist/extensions/pi-zentui/test/responsive-dependencies.test.ts +4 -4
- package/dist/extensions/pi-zentui/test/settings-command.test.ts +289 -7
- package/dist/extensions/pi-zentui/test/settings-previews.test.ts +106 -0
- package/dist/extensions/pi-zentui/test/thinking-experimental-docs.test.ts +51 -0
- package/dist/extensions/pi-zentui/test/thinking-experimental-loader-diagnostics.mjs +54 -0
- package/dist/extensions/pi-zentui/test/thinking-experimental-missing-export.test.ts +44 -0
- package/dist/extensions/pi-zentui/test/thinking-experimental-rows.test.ts +297 -0
- package/dist/extensions/pi-zentui/test/thinking-experimental-tui.mjs +2329 -0
- package/dist/extensions/pi-zentui/test/thinking-experimental.test.ts +1668 -0
- package/dist/extensions/pi-zentui/test/thinking-status.test.ts +85 -0
- package/dist/extensions/pi-zentui/test/thinking-steps.test.ts +122 -0
- package/dist/extensions/pi-zentui/test/user-message-osc.test.ts +58 -0
- package/dist/skills/brandkit/SKILL.md +0 -2
- package/dist/skills/{industrial-brutalist-ui → brutalist}/SKILL.md +1 -3
- package/dist/skills/gpt-taste/SKILL.md +0 -2
- package/dist/skills/image-to-code/SKILL.md +0 -2
- package/dist/skills/imagegen-frontend-mobile/SKILL.md +0 -2
- package/dist/skills/imagegen-frontend-web/SKILL.md +0 -2
- package/dist/skills/{minimalist-ui → minimalist}/SKILL.md +1 -3
- package/dist/skills/{full-output-enforcement → output}/SKILL.md +1 -3
- package/dist/skills/{redesign-existing-projects → redesign}/SKILL.md +1 -3
- package/dist/skills/{high-end-visual-design → soft}/SKILL.md +1 -3
- package/dist/skills/stitch/DESIGN.md +121 -0
- package/dist/skills/{stitch-design-taste → stitch}/SKILL.md +1 -3
- package/dist/skills/{design-taste-frontend → taste}/SKILL.md +1 -3
- package/dist/skills/{design-taste-frontend-v1 → taste-v1}/SKILL.md +2 -4
- package/package.json +1 -1
|
@@ -9,7 +9,8 @@ import {
|
|
|
9
9
|
watchdogWarningFromLspDiagnostics,
|
|
10
10
|
type WatchdogLspDiagnosticsFunction,
|
|
11
11
|
} from "./lsp-diagnostics.ts";
|
|
12
|
-
import {
|
|
12
|
+
import { ruleViolationWarning, type WatchdogRuleViolation } from "./rules.ts";
|
|
13
|
+
import { WatchdogScopeArtifact } from "./scope.ts";
|
|
13
14
|
import { resolveWatchdogConfig } from "./settings.ts";
|
|
14
15
|
import { formatWatchdogTurnDelta } from "./turn-delta.ts";
|
|
15
16
|
import {
|
|
@@ -62,9 +63,8 @@ export interface WatchdogRuntimeSnapshot {
|
|
|
62
63
|
staleReviews: number;
|
|
63
64
|
reviewConnected: boolean;
|
|
64
65
|
reviewDescription: string;
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
autoFollowStalemate: boolean;
|
|
66
|
+
boundaryRepeats: number;
|
|
67
|
+
stalemate: boolean;
|
|
68
68
|
reviewTrigger: "turn-delta" | "repo-edits";
|
|
69
69
|
changedPaths?: string[];
|
|
70
70
|
lsp: WatchdogLspRuntimeSnapshot;
|
|
@@ -80,20 +80,29 @@ interface MainWatchdogRuntimeOptions {
|
|
|
80
80
|
resolveConfig?: (cwd: string, options?: { session?: Record<string, unknown> }) => WatchdogSettingsResult;
|
|
81
81
|
review?: WatchdogReviewFunction;
|
|
82
82
|
reviewDescription?: string;
|
|
83
|
-
displayWarning?: (warning: WatchdogWarningDetails, options?:
|
|
84
|
-
sendUserMessage?: (message: string) => void | Promise<void>;
|
|
83
|
+
displayWarning?: (warning: WatchdogWarningDetails, options?: WatchdogWarningSendOptions) => void;
|
|
85
84
|
reviewChangesOnly?: boolean;
|
|
86
85
|
lspDiagnostics?: WatchdogLspDiagnosticsFunction;
|
|
87
86
|
repoChangeSignature?: typeof computeWatchdogRepoChangeSignature;
|
|
88
87
|
}
|
|
89
88
|
|
|
89
|
+
export type WatchdogWarningSendOptions = { deliverAs: "steer" } | { triggerTurn: false };
|
|
90
|
+
|
|
90
91
|
type ContextLike = Pick<ExtensionContext, "cwd">;
|
|
91
92
|
type ReviewDeltaOutcome = "completed" | "timeout" | "stale";
|
|
92
93
|
|
|
93
94
|
const DEFAULT_REVIEW: WatchdogReviewFunction = () => ({ warnings: [] });
|
|
94
95
|
const MAX_REVIEW_INPUT_CHARS = 24_000;
|
|
96
|
+
const REVIEW_INPUT_HEAD_CHARS = 6_000;
|
|
95
97
|
const REVIEW_DELTA_SEPARATOR = "\n\n---\n\n";
|
|
96
98
|
|
|
99
|
+
export function boundWatchdogReviewText(text: string, cap = MAX_REVIEW_INPUT_CHARS): string {
|
|
100
|
+
if (text.length <= cap) return text;
|
|
101
|
+
const head = Math.min(REVIEW_INPUT_HEAD_CHARS, Math.floor(cap / 4));
|
|
102
|
+
const marker = `\n\n[... about ${text.length - cap} characters omitted ...]\n\n`;
|
|
103
|
+
return `${text.slice(0, head)}${marker}${text.slice(text.length - (cap - head - marker.length))}`;
|
|
104
|
+
}
|
|
105
|
+
|
|
97
106
|
function errorMessage(error: unknown): string {
|
|
98
107
|
return error instanceof Error ? error.message : String(error);
|
|
99
108
|
}
|
|
@@ -116,8 +125,7 @@ export class MainWatchdogRuntime {
|
|
|
116
125
|
private readonly review: WatchdogReviewFunction;
|
|
117
126
|
private readonly reviewConnected: boolean;
|
|
118
127
|
private readonly reviewDescription: string;
|
|
119
|
-
private readonly displayWarning: ((warning: WatchdogWarningDetails, options?:
|
|
120
|
-
private readonly sendUserMessage: ((message: string) => void | Promise<void>) | undefined;
|
|
128
|
+
private readonly displayWarning: ((warning: WatchdogWarningDetails, options?: WatchdogWarningSendOptions) => void) | undefined;
|
|
121
129
|
private readonly reviewChangesOnly: boolean;
|
|
122
130
|
private readonly lspDiagnostics: WatchdogLspDiagnosticsFunction;
|
|
123
131
|
private readonly repoChangeSignature: typeof computeWatchdogRepoChangeSignature;
|
|
@@ -145,7 +153,6 @@ export class MainWatchdogRuntime {
|
|
|
145
153
|
private userPrompt: string | undefined;
|
|
146
154
|
private waiters: Waiter[] = [];
|
|
147
155
|
private lastWarning: WatchdogWarningDetails | undefined;
|
|
148
|
-
private displayedWarningSequence = 0;
|
|
149
156
|
private lastError: string | undefined;
|
|
150
157
|
private lastReviewInputSignature: string | undefined;
|
|
151
158
|
private turnStartChangeSignature: WatchdogRepoChangeSignature | undefined;
|
|
@@ -155,12 +162,10 @@ export class MainWatchdogRuntime {
|
|
|
155
162
|
private observedRepoEditThisTurn = false;
|
|
156
163
|
private toolResultsThisRun = 0;
|
|
157
164
|
private midRunReviewing = false;
|
|
158
|
-
private
|
|
159
|
-
private
|
|
160
|
-
private
|
|
161
|
-
private
|
|
162
|
-
private autoFollowStalemate = false;
|
|
163
|
-
private pendingAutoFollowPrompts: string[] = [];
|
|
165
|
+
private lastBoundaryIdentity: string | undefined;
|
|
166
|
+
private boundaryRepeats = 0;
|
|
167
|
+
private stalemate = false;
|
|
168
|
+
private ruleWarningsThisRun = new Set<string>();
|
|
164
169
|
private midRunGeneration = 0;
|
|
165
170
|
private activeReviewAbortController: AbortController | undefined;
|
|
166
171
|
private failedReviews = 0;
|
|
@@ -173,7 +178,6 @@ export class MainWatchdogRuntime {
|
|
|
173
178
|
this.reviewConnected = Boolean(options.review);
|
|
174
179
|
this.reviewDescription = options.reviewDescription ?? (options.review ? "injected seam" : "not wired");
|
|
175
180
|
this.displayWarning = options.displayWarning;
|
|
176
|
-
this.sendUserMessage = options.sendUserMessage;
|
|
177
181
|
this.reviewChangesOnly = options.reviewChangesOnly === true;
|
|
178
182
|
this.lspDiagnostics = options.lspDiagnostics ?? collectWatchdogLspDiagnostics;
|
|
179
183
|
this.repoChangeSignature = options.repoChangeSignature ?? computeWatchdogRepoChangeSignature;
|
|
@@ -189,7 +193,8 @@ export class MainWatchdogRuntime {
|
|
|
189
193
|
this.sessionOverrideEnabled = undefined;
|
|
190
194
|
this.sessionModelOverride = undefined;
|
|
191
195
|
this.refreshConfig(ctx.cwd);
|
|
192
|
-
this.reset("session_start", { clearReviewInputSignature: true, resetChangeSignature: true, clearLspLedger: true, clearScope: true
|
|
196
|
+
this.reset("session_start", { clearReviewInputSignature: true, resetChangeSignature: true, clearLspLedger: true, clearScope: true });
|
|
197
|
+
this.resetBoundaryRepeats();
|
|
193
198
|
}
|
|
194
199
|
|
|
195
200
|
refreshConfig(cwd = this.cwd): WatchdogSettingsResult {
|
|
@@ -247,7 +252,7 @@ export class MainWatchdogRuntime {
|
|
|
247
252
|
return this.getSnapshot();
|
|
248
253
|
}
|
|
249
254
|
|
|
250
|
-
reset(_reason = "reset", options: { clearReviewInputSignature?: boolean; resetChangeSignature?: boolean; clearLspLedger?: boolean; clearScope?: boolean
|
|
255
|
+
reset(_reason = "reset", options: { clearReviewInputSignature?: boolean; resetChangeSignature?: boolean; clearLspLedger?: boolean; clearScope?: boolean } = {}): void {
|
|
251
256
|
this.abortActiveAgentEnd();
|
|
252
257
|
this.epoch++;
|
|
253
258
|
this.status = "idle";
|
|
@@ -263,16 +268,12 @@ export class MainWatchdogRuntime {
|
|
|
263
268
|
this.observedRepoEditThisTurn = false;
|
|
264
269
|
this.toolResultsThisRun = 0;
|
|
265
270
|
this.midRunReviewing = false;
|
|
266
|
-
this.
|
|
271
|
+
this.ruleWarningsThisRun.clear();
|
|
267
272
|
if (options.clearLspLedger) {
|
|
268
273
|
this.lspLedger.reset();
|
|
269
274
|
this.lastLspSnapshot = undefined;
|
|
270
275
|
}
|
|
271
|
-
if (options.clearScope)
|
|
272
|
-
this.scope.reset();
|
|
273
|
-
this.pendingAutoFollowPrompts = [];
|
|
274
|
-
}
|
|
275
|
-
if (options.resetAutoFollow) this.resetAutoFollowState();
|
|
276
|
+
if (options.clearScope) this.scope.reset();
|
|
276
277
|
if (options.clearReviewInputSignature) this.lastReviewInputSignature = undefined;
|
|
277
278
|
if (options.resetChangeSignature) this.resetRepoChangeBaseline({ reviewed: true });
|
|
278
279
|
this.guard.reset();
|
|
@@ -297,22 +298,17 @@ export class MainWatchdogRuntime {
|
|
|
297
298
|
this.observedRepoEditThisTurn = false;
|
|
298
299
|
this.toolResultsThisRun = 0;
|
|
299
300
|
this.midRunReviewing = false;
|
|
300
|
-
this.autoFollowQueued = false;
|
|
301
301
|
this.resolveWaiters(false);
|
|
302
302
|
}
|
|
303
303
|
|
|
304
304
|
handleBeforeAgentStart(event: unknown, ctx: ContextLike): void {
|
|
305
305
|
if (this.disposed) return;
|
|
306
306
|
const incomingPrompt = promptFromBeforeAgentStart(event);
|
|
307
|
-
|
|
308
|
-
|
|
309
|
-
const pendingIndex = incomingPrompt === undefined ? -1 : this.pendingAutoFollowPrompts.indexOf(incomingPrompt);
|
|
310
|
-
const autoFollowPrompt = pendingIndex >= 0 || isWatchdogAutoFollowPromptEvent(event);
|
|
311
|
-
if (pendingIndex >= 0) this.pendingAutoFollowPrompts.splice(pendingIndex, 1);
|
|
312
|
-
this.reset("before_agent_start", { resetAutoFollow: !autoFollowPrompt });
|
|
307
|
+
this.reset("before_agent_start");
|
|
308
|
+
this.resetBoundaryRepeats();
|
|
313
309
|
this.refreshConfig(ctx.cwd);
|
|
314
310
|
this.userPrompt = incomingPrompt;
|
|
315
|
-
if (
|
|
311
|
+
if (this.userPrompt?.trim()) {
|
|
316
312
|
this.includeUserPromptInNextDelta = true;
|
|
317
313
|
this.scope.addPrompt(this.userPrompt);
|
|
318
314
|
} else {
|
|
@@ -383,8 +379,6 @@ export class MainWatchdogRuntime {
|
|
|
383
379
|
const lspAbortController = new AbortController();
|
|
384
380
|
this.activeAgentEndId = agentEndId;
|
|
385
381
|
this.activeAgentEndAbortController = lspAbortController;
|
|
386
|
-
let displayedDuringAgentEnd: WatchdogWarningDetails | undefined;
|
|
387
|
-
const previousDisplayedSequence = this.displayedWarningSequence;
|
|
388
382
|
try {
|
|
389
383
|
this.guard.startModelUpdate();
|
|
390
384
|
const lspBlock = await this.collectLspDiagnostics(changeSignature, {
|
|
@@ -425,8 +419,6 @@ export class MainWatchdogRuntime {
|
|
|
425
419
|
this.currentChangedPaths = changeSignature?.changedPaths;
|
|
426
420
|
this.status = "idle";
|
|
427
421
|
}
|
|
428
|
-
displayedDuringAgentEnd = this.displayedWarningSequence !== previousDisplayedSequence ? this.lastWarning : undefined;
|
|
429
|
-
this.queueAutoFollowIfNeeded(displayedDuringAgentEnd);
|
|
430
422
|
this.resolveWaiters(true);
|
|
431
423
|
} finally {
|
|
432
424
|
if (this.activeAgentEndAbortController === lspAbortController) this.activeAgentEndAbortController = undefined;
|
|
@@ -434,6 +426,14 @@ export class MainWatchdogRuntime {
|
|
|
434
426
|
}
|
|
435
427
|
}
|
|
436
428
|
|
|
429
|
+
displayRuleWarning(violation: WatchdogRuleViolation): void {
|
|
430
|
+
if (this.disposed || this.ruleWarningsThisRun.has(violation.summary)) return;
|
|
431
|
+
this.ruleWarningsThisRun.add(violation.summary);
|
|
432
|
+
const details = normalizeWatchdogWarningDetails(ruleViolationWarning(violation), { state: "displayed", displayedAt: new Date().toISOString() });
|
|
433
|
+
this.lastWarning = details;
|
|
434
|
+
this.displayWarning?.(details, { deliverAs: "steer" });
|
|
435
|
+
}
|
|
436
|
+
|
|
437
437
|
recordDisplayedWarning(warning: WatchdogWarning): WatchdogWarningDetails {
|
|
438
438
|
const details = normalizeWatchdogWarningDetails(warning, { state: "displayed", source: warning.source ?? "main" });
|
|
439
439
|
this.lastWarning = details;
|
|
@@ -460,9 +460,8 @@ export class MainWatchdogRuntime {
|
|
|
460
460
|
staleReviews: this.staleReviews,
|
|
461
461
|
reviewConnected: this.reviewConnected,
|
|
462
462
|
reviewDescription: this.reviewDescription,
|
|
463
|
-
|
|
464
|
-
|
|
465
|
-
autoFollowStalemate: this.autoFollowStalemate,
|
|
463
|
+
boundaryRepeats: this.boundaryRepeats,
|
|
464
|
+
stalemate: this.stalemate,
|
|
466
465
|
reviewTrigger: this.reviewChangesOnly ? "repo-edits" : "turn-delta",
|
|
467
466
|
...(this.currentChangedPaths?.length ? { changedPaths: [...this.currentChangedPaths] } : {}),
|
|
468
467
|
lsp: this.lspSnapshot(),
|
|
@@ -497,7 +496,7 @@ export class MainWatchdogRuntime {
|
|
|
497
496
|
|
|
498
497
|
private acceptWarning(epoch: number, reviewId: number, warning: WatchdogWarning): boolean {
|
|
499
498
|
if (!this.isCurrent(epoch, reviewId) || !this.isEnabled() || !this.warningMeetsThreshold(warning)) return false;
|
|
500
|
-
const decision = this.guard.evaluate(warning);
|
|
499
|
+
const decision = this.guard.evaluate(warning, { allowRepeatOf: this.repeatableBoundaryIdentity() });
|
|
501
500
|
if (!decision.accepted) return false;
|
|
502
501
|
const details = normalizeWatchdogWarningDetails(warning, {
|
|
503
502
|
state: "candidate",
|
|
@@ -511,7 +510,7 @@ export class MainWatchdogRuntime {
|
|
|
511
510
|
|
|
512
511
|
private displayBoundaryWarning(warning: WatchdogWarning): boolean {
|
|
513
512
|
if (!this.isEnabled() || !this.warningMeetsThreshold(warning)) return false;
|
|
514
|
-
const decision = this.guard.evaluate(warning);
|
|
513
|
+
const decision = this.guard.evaluate(warning, { allowRepeatOf: this.repeatableBoundaryIdentity() });
|
|
515
514
|
if (!decision.accepted) return false;
|
|
516
515
|
const details = normalizeWatchdogWarningDetails(warning, {
|
|
517
516
|
state: "displayed",
|
|
@@ -519,9 +518,7 @@ export class MainWatchdogRuntime {
|
|
|
519
518
|
identity: decision.identity,
|
|
520
519
|
displayedAt: new Date().toISOString(),
|
|
521
520
|
});
|
|
522
|
-
this.
|
|
523
|
-
this.displayedWarningSequence++;
|
|
524
|
-
this.displayWarning?.(details);
|
|
521
|
+
this.deliverBoundaryWarning(details);
|
|
525
522
|
return true;
|
|
526
523
|
}
|
|
527
524
|
|
|
@@ -635,51 +632,41 @@ export class MainWatchdogRuntime {
|
|
|
635
632
|
state: "displayed",
|
|
636
633
|
displayedAt: new Date().toISOString(),
|
|
637
634
|
};
|
|
638
|
-
|
|
639
|
-
|
|
640
|
-
|
|
641
|
-
|
|
642
|
-
|
|
643
|
-
|
|
644
|
-
this.autoFollowQueued = false;
|
|
645
|
-
this.autoFollowAttempts = 0;
|
|
646
|
-
this.consecutiveAutoFollowIdentity = undefined;
|
|
647
|
-
this.consecutiveAutoFollowRepeats = 0;
|
|
648
|
-
this.autoFollowStalemate = false;
|
|
635
|
+
if (correction) {
|
|
636
|
+
this.lastWarning = details;
|
|
637
|
+
this.displayWarning?.(details, { deliverAs: "steer" });
|
|
638
|
+
return;
|
|
639
|
+
}
|
|
640
|
+
this.deliverBoundaryWarning(details);
|
|
649
641
|
}
|
|
650
642
|
|
|
651
|
-
|
|
652
|
-
|
|
653
|
-
|
|
654
|
-
|
|
643
|
+
// A displayed boundary warning continues the run; the same identity back from consecutive
|
|
644
|
+
// boundaries means no progress, so it is shown held instead of continuing again.
|
|
645
|
+
private deliverBoundaryWarning(details: WatchdogWarningDetails): void {
|
|
646
|
+
const identity = details.identity ?? reviewInputSignature([details.severity, details.summary, details.evidence].join("\n"));
|
|
647
|
+
if (this.lastBoundaryIdentity === identity) this.boundaryRepeats++;
|
|
655
648
|
else {
|
|
656
|
-
this.
|
|
657
|
-
this.
|
|
658
|
-
}
|
|
659
|
-
if (this.consecutiveAutoFollowRepeats >= this.configResult.config.autoFollow.stalemateRepeats) {
|
|
660
|
-
this.autoFollowStalemate = true;
|
|
661
|
-
this.lastWarning = { ...warning, state: "stalemate", stalemateRepeats: this.consecutiveAutoFollowRepeats };
|
|
662
|
-
return;
|
|
649
|
+
this.lastBoundaryIdentity = identity;
|
|
650
|
+
this.boundaryRepeats = 1;
|
|
663
651
|
}
|
|
664
|
-
const
|
|
665
|
-
|
|
666
|
-
|
|
667
|
-
|
|
668
|
-
this.
|
|
669
|
-
|
|
670
|
-
|
|
671
|
-
|
|
672
|
-
|
|
673
|
-
|
|
674
|
-
|
|
675
|
-
this.
|
|
676
|
-
|
|
677
|
-
|
|
678
|
-
|
|
679
|
-
|
|
680
|
-
|
|
681
|
-
|
|
682
|
-
});
|
|
652
|
+
const stalemate = this.boundaryRepeats >= this.configResult.config.stalemateRepeats;
|
|
653
|
+
const delivered: WatchdogWarningDetails = stalemate
|
|
654
|
+
? { ...details, state: "stalemate", stalemateRepeats: this.boundaryRepeats }
|
|
655
|
+
: details;
|
|
656
|
+
this.stalemate = stalemate;
|
|
657
|
+
this.lastWarning = delivered;
|
|
658
|
+
this.displayWarning?.(delivered, stalemate ? { triggerTurn: false } : undefined);
|
|
659
|
+
}
|
|
660
|
+
|
|
661
|
+
// The previous boundary finding is a repeat to count, not a duplicate, until stalemate.
|
|
662
|
+
private repeatableBoundaryIdentity(): string | undefined {
|
|
663
|
+
return this.waitingAtAgentEnd && !this.stalemate ? this.lastBoundaryIdentity : undefined;
|
|
664
|
+
}
|
|
665
|
+
|
|
666
|
+
private resetBoundaryRepeats(): void {
|
|
667
|
+
this.lastBoundaryIdentity = undefined;
|
|
668
|
+
this.boundaryRepeats = 0;
|
|
669
|
+
this.stalemate = false;
|
|
683
670
|
}
|
|
684
671
|
|
|
685
672
|
private currentRepoChangeSignature(cwd = this.cwd): WatchdogRepoChangeSignature | undefined {
|
|
@@ -783,7 +770,7 @@ export class MainWatchdogRuntime {
|
|
|
783
770
|
private appendBoundedDelta(delta: string): void {
|
|
784
771
|
let entry = delta.trim();
|
|
785
772
|
if (!entry) return;
|
|
786
|
-
if (entry.length > MAX_REVIEW_INPUT_CHARS) entry = entry
|
|
773
|
+
if (entry.length > MAX_REVIEW_INPUT_CHARS) entry = boundWatchdogReviewText(entry);
|
|
787
774
|
this.pendingDeltas.push(entry);
|
|
788
775
|
this.pendingDeltaChars += entry.length;
|
|
789
776
|
while (this.pendingDeltas.length > 1 && this.pendingDeltaChars + (this.pendingDeltas.length - 1) * REVIEW_DELTA_SEPARATOR.length > MAX_REVIEW_INPUT_CHARS) {
|
|
@@ -799,7 +786,7 @@ export class MainWatchdogRuntime {
|
|
|
799
786
|
? ["Changed repo paths:", ...changeSignature.changedPaths.slice(0, 200).map((file) => `- ${file}`)].join("\n")
|
|
800
787
|
: "";
|
|
801
788
|
const contextPieces = [scopeBlock, changes, lspBlock].filter(Boolean);
|
|
802
|
-
if (!contextPieces.length) return input
|
|
789
|
+
if (!contextPieces.length) return boundWatchdogReviewText(input);
|
|
803
790
|
|
|
804
791
|
const maxContextLength = Math.floor(MAX_REVIEW_INPUT_CHARS / 2);
|
|
805
792
|
const maxPieceLength = Math.max(1_000, Math.floor(maxContextLength / contextPieces.length));
|
|
@@ -808,11 +795,7 @@ export class MainWatchdogRuntime {
|
|
|
808
795
|
: piece).join(REVIEW_DELTA_SEPARATOR);
|
|
809
796
|
const separatorLength = input ? REVIEW_DELTA_SEPARATOR.length : 0;
|
|
810
797
|
const inputBudget = MAX_REVIEW_INPUT_CHARS - boundedContext.length - separatorLength;
|
|
811
|
-
const boundedInput = inputBudget <= 0
|
|
812
|
-
? ""
|
|
813
|
-
: input.length > inputBudget
|
|
814
|
-
? input.slice(-inputBudget)
|
|
815
|
-
: input;
|
|
798
|
+
const boundedInput = inputBudget <= 0 ? "" : boundWatchdogReviewText(input, inputBudget);
|
|
816
799
|
return [boundedContext, boundedInput].filter(Boolean).join(REVIEW_DELTA_SEPARATOR);
|
|
817
800
|
}
|
|
818
801
|
|
|
@@ -59,4 +59,5 @@ export function isWatchdogAutoFollowPromptEvent(event: unknown): boolean {
|
|
|
59
59
|
export function markWatchdogAutoFollowPromptEvent<T extends object>(event: T): T {
|
|
60
60
|
Object.defineProperty(event, WATCHDOG_AUTO_FOLLOW_PROMPT_MARKER, { value: true });
|
|
61
61
|
return event;
|
|
62
|
-
}
|
|
62
|
+
}
|
|
63
|
+
|
|
@@ -3,39 +3,31 @@ import * as path from "node:path";
|
|
|
3
3
|
import { THINKING_LEVELS, type ThinkingLevel } from "../shared/model-info.ts";
|
|
4
4
|
import { getAgentDir, getProjectConfigDir } from "../shared/utils.ts";
|
|
5
5
|
import {
|
|
6
|
-
WATCHDOG_DELIVERY_MODES,
|
|
7
|
-
WATCHDOG_LATE_WARNING_POLICIES,
|
|
8
6
|
WATCHDOG_WARNING_SEVERITIES,
|
|
9
7
|
type ResolvedWatchdogConfig,
|
|
10
|
-
type WatchdogAsyncCompletionConfig,
|
|
11
|
-
type WatchdogAutoFollowConfig,
|
|
12
8
|
type WatchdogChildOverrideConfig,
|
|
13
9
|
type WatchdogChildrenConfig,
|
|
14
|
-
type WatchdogDeliveryMode,
|
|
15
10
|
type WatchdogEndpointConfig,
|
|
16
11
|
type WatchdogCadenceConfig,
|
|
17
12
|
type WatchdogGuidanceConfig,
|
|
18
|
-
type WatchdogLateWarningPolicy,
|
|
19
13
|
type WatchdogScopeConfig,
|
|
20
14
|
type WatchdogLspConfig,
|
|
15
|
+
type WatchdogRoleModelRule,
|
|
16
|
+
type WatchdogRulesConfig,
|
|
21
17
|
type WatchdogSettingsError,
|
|
22
18
|
type WatchdogSettingsResult,
|
|
23
19
|
type WatchdogSettingsSource,
|
|
24
20
|
type WatchdogSeverity,
|
|
25
|
-
type WatchdogSyncBacklog,
|
|
26
21
|
} from "./types.ts";
|
|
27
22
|
|
|
28
|
-
type WatchdogAutoFollowPatch = Partial<WatchdogAutoFollowConfig>;
|
|
29
23
|
type WatchdogGuidancePatch = Partial<WatchdogGuidanceConfig>;
|
|
30
24
|
type WatchdogScopePatch = Partial<WatchdogScopeConfig>;
|
|
31
25
|
type WatchdogCadencePatch = Partial<WatchdogCadenceConfig>;
|
|
32
26
|
type WatchdogEndpointPatch = Partial<WatchdogEndpointConfig>;
|
|
33
27
|
type WatchdogChildOverridePatch = Partial<WatchdogChildOverrideConfig>;
|
|
34
|
-
type WatchdogChildrenPatch = Partial<Omit<WatchdogChildrenConfig, "
|
|
35
|
-
autoFollow?: WatchdogAutoFollowPatch;
|
|
28
|
+
type WatchdogChildrenPatch = Partial<Omit<WatchdogChildrenConfig, "overrides">> & {
|
|
36
29
|
overrides?: Record<string, WatchdogChildOverridePatch>;
|
|
37
30
|
};
|
|
38
|
-
type WatchdogAsyncCompletionPatch = Partial<WatchdogAsyncCompletionConfig>;
|
|
39
31
|
type WatchdogLspPatch = Partial<WatchdogLspConfig>;
|
|
40
32
|
|
|
41
33
|
export type WatchdogSettingsWriteScope = "user" | "project";
|
|
@@ -52,14 +44,13 @@ export interface WatchdogModelSettingsWrite {
|
|
|
52
44
|
thinking?: ThinkingLevel | false | null;
|
|
53
45
|
}
|
|
54
46
|
|
|
55
|
-
type WatchdogConfigPatch = Partial<Omit<ResolvedWatchdogConfig, "guidance" | "
|
|
47
|
+
type WatchdogConfigPatch = Partial<Omit<ResolvedWatchdogConfig, "guidance" | "scope" | "cadence" | "main" | "children" | "lsp" | "rules">> & {
|
|
48
|
+
rules?: WatchdogRulesConfig;
|
|
56
49
|
guidance?: WatchdogGuidancePatch;
|
|
57
|
-
autoFollow?: WatchdogAutoFollowPatch;
|
|
58
50
|
scope?: WatchdogScopePatch;
|
|
59
51
|
cadence?: WatchdogCadencePatch;
|
|
60
52
|
main?: WatchdogEndpointPatch;
|
|
61
53
|
children?: WatchdogChildrenPatch;
|
|
62
|
-
asyncCompletion?: WatchdogAsyncCompletionPatch;
|
|
63
54
|
lsp?: WatchdogLspPatch;
|
|
64
55
|
};
|
|
65
56
|
|
|
@@ -70,22 +61,13 @@ interface ParseMeta {
|
|
|
70
61
|
|
|
71
62
|
export const DEFAULT_WATCHDOG_CONFIG: ResolvedWatchdogConfig = {
|
|
72
63
|
enabled: false,
|
|
73
|
-
delivery: "held",
|
|
74
|
-
showDuringRun: false,
|
|
75
|
-
syncBacklog: "off",
|
|
76
64
|
agentEndTimeoutMs: 30_000,
|
|
77
|
-
lateWarningPolicy: "show-stale-no-autofollow",
|
|
78
65
|
severityThreshold: "concern",
|
|
79
66
|
maxWarnings: null,
|
|
80
67
|
guidance: {
|
|
81
68
|
watchdogMd: true,
|
|
82
|
-
systemPromptPath: null,
|
|
83
|
-
},
|
|
84
|
-
autoFollow: {
|
|
85
|
-
blockers: true,
|
|
86
|
-
maxAttempts: 3,
|
|
87
|
-
stalemateRepeats: 3,
|
|
88
69
|
},
|
|
70
|
+
stalemateRepeats: 3,
|
|
89
71
|
scope: {
|
|
90
72
|
enabled: true,
|
|
91
73
|
},
|
|
@@ -98,73 +80,51 @@ export const DEFAULT_WATCHDOG_CONFIG: ResolvedWatchdogConfig = {
|
|
|
98
80
|
children: {
|
|
99
81
|
enabled: false,
|
|
100
82
|
watchdogTailTimeoutMs: 120_000,
|
|
101
|
-
autoFollow: {
|
|
102
|
-
blockers: true,
|
|
103
|
-
maxAttempts: 3,
|
|
104
|
-
stalemateRepeats: 3,
|
|
105
|
-
},
|
|
106
83
|
overrides: {},
|
|
107
84
|
},
|
|
108
|
-
asyncCompletion: {
|
|
109
|
-
enabled: false,
|
|
110
|
-
autoFollowBlockers: false,
|
|
111
|
-
},
|
|
112
85
|
lsp: {
|
|
113
86
|
enabled: true,
|
|
114
87
|
timeoutMs: 3_000,
|
|
115
88
|
maxFiles: 20,
|
|
116
89
|
maxDiagnostics: 50,
|
|
117
90
|
},
|
|
118
|
-
compactAtPercent: 80,
|
|
119
|
-
reviewRetryDelayMs: 1_000,
|
|
120
|
-
maxReviewFailures: 3,
|
|
121
91
|
};
|
|
122
92
|
|
|
123
93
|
const WATCHDOG_FIELDS = new Set([
|
|
124
94
|
"enabled",
|
|
125
|
-
"delivery",
|
|
126
|
-
"showDuringRun",
|
|
127
|
-
"syncBacklog",
|
|
128
95
|
"agentEndTimeoutMs",
|
|
129
|
-
"lateWarningPolicy",
|
|
130
96
|
"severityThreshold",
|
|
131
97
|
"maxWarnings",
|
|
132
98
|
"guidance",
|
|
133
|
-
"
|
|
99
|
+
"stalemateRepeats",
|
|
134
100
|
"scope",
|
|
135
101
|
"cadence",
|
|
136
102
|
"main",
|
|
137
103
|
"children",
|
|
138
|
-
"asyncCompletion",
|
|
139
104
|
"lsp",
|
|
140
|
-
"
|
|
141
|
-
"reviewRetryDelayMs",
|
|
142
|
-
"maxReviewFailures",
|
|
105
|
+
"rules",
|
|
143
106
|
]);
|
|
144
|
-
const
|
|
145
|
-
const
|
|
107
|
+
const RULES_FIELDS = new Set(["action", "roleModels"]);
|
|
108
|
+
const ROLE_MODEL_RULE_FIELDS = new Set(["allow", "deny", "note"]);
|
|
109
|
+
const GUIDANCE_FIELDS = new Set(["watchdogMd"]);
|
|
146
110
|
const SCOPE_FIELDS = new Set(["enabled"]);
|
|
147
111
|
const CADENCE_FIELDS = new Set(["everyNTools"]);
|
|
148
112
|
const ENDPOINT_FIELDS = new Set(["enabled", "model", "thinking"]);
|
|
149
|
-
const CHILDREN_FIELDS = new Set(["enabled", "model", "thinking", "watchdogTailTimeoutMs", "
|
|
150
|
-
const CHILD_OVERRIDE_FIELDS = new Set(["enabled", "model", "thinking"]);
|
|
151
|
-
const ASYNC_COMPLETION_FIELDS = new Set(["enabled", "autoFollowBlockers"]);
|
|
113
|
+
const CHILDREN_FIELDS = new Set(["enabled", "model", "thinking", "watchdogTailTimeoutMs", "cadence", "overrides"]);
|
|
114
|
+
const CHILD_OVERRIDE_FIELDS = new Set(["enabled", "model", "thinking", "cadence"]);
|
|
152
115
|
const LSP_FIELDS = new Set(["enabled", "timeoutMs", "maxFiles", "maxDiagnostics"]);
|
|
153
116
|
|
|
154
117
|
function cloneDefaultConfig(): ResolvedWatchdogConfig {
|
|
155
118
|
return {
|
|
156
119
|
...DEFAULT_WATCHDOG_CONFIG,
|
|
157
120
|
guidance: { ...DEFAULT_WATCHDOG_CONFIG.guidance },
|
|
158
|
-
autoFollow: { ...DEFAULT_WATCHDOG_CONFIG.autoFollow },
|
|
159
121
|
scope: { ...DEFAULT_WATCHDOG_CONFIG.scope },
|
|
160
122
|
cadence: { ...DEFAULT_WATCHDOG_CONFIG.cadence },
|
|
161
123
|
main: { ...DEFAULT_WATCHDOG_CONFIG.main },
|
|
162
124
|
children: {
|
|
163
125
|
...DEFAULT_WATCHDOG_CONFIG.children,
|
|
164
|
-
autoFollow: { ...DEFAULT_WATCHDOG_CONFIG.children.autoFollow },
|
|
165
126
|
overrides: {},
|
|
166
127
|
},
|
|
167
|
-
asyncCompletion: { ...DEFAULT_WATCHDOG_CONFIG.asyncCompletion },
|
|
168
128
|
lsp: { ...DEFAULT_WATCHDOG_CONFIG.lsp },
|
|
169
129
|
};
|
|
170
130
|
}
|
|
@@ -227,35 +187,11 @@ function parseEnum<T extends string>(value: unknown, field: string, meta: ParseM
|
|
|
227
187
|
throw invalid(meta, field, values.map((item) => `'${item}'`).join(" or "));
|
|
228
188
|
}
|
|
229
189
|
|
|
230
|
-
function parseSyncBacklog(value: unknown, field: string, meta: ParseMeta): WatchdogSyncBacklog {
|
|
231
|
-
if (value === "off") return "off";
|
|
232
|
-
return parseInteger(value, field, meta, "'off' or a positive integer", (candidate) => candidate >= 1);
|
|
233
|
-
}
|
|
234
|
-
|
|
235
190
|
function parseGuidancePatch(value: unknown, field: string, meta: ParseMeta): WatchdogGuidancePatch {
|
|
236
191
|
const input = parseObject(value, field, meta);
|
|
237
192
|
assertKnownFields(input, GUIDANCE_FIELDS, field, meta);
|
|
238
193
|
const patch: WatchdogGuidancePatch = {};
|
|
239
194
|
if ("watchdogMd" in input) patch.watchdogMd = parseBoolean(input.watchdogMd, `${field}.watchdogMd`, meta);
|
|
240
|
-
if ("systemPromptPath" in input) {
|
|
241
|
-
patch.systemPromptPath = input.systemPromptPath === null
|
|
242
|
-
? null
|
|
243
|
-
: parseNonEmptyString(input.systemPromptPath, `${field}.systemPromptPath`, meta);
|
|
244
|
-
}
|
|
245
|
-
return patch;
|
|
246
|
-
}
|
|
247
|
-
|
|
248
|
-
function parseAutoFollowPatch(value: unknown, field: string, meta: ParseMeta): WatchdogAutoFollowPatch {
|
|
249
|
-
const input = parseObject(value, field, meta);
|
|
250
|
-
assertKnownFields(input, AUTO_FOLLOW_FIELDS, field, meta);
|
|
251
|
-
const patch: WatchdogAutoFollowPatch = {};
|
|
252
|
-
if ("blockers" in input) patch.blockers = parseBoolean(input.blockers, `${field}.blockers`, meta);
|
|
253
|
-
if ("maxAttempts" in input) {
|
|
254
|
-
patch.maxAttempts = parseNullableInteger(input.maxAttempts, `${field}.maxAttempts`, meta, "null or a positive integer", (candidate) => candidate >= 1);
|
|
255
|
-
}
|
|
256
|
-
if ("stalemateRepeats" in input) {
|
|
257
|
-
patch.stalemateRepeats = parseInteger(input.stalemateRepeats, `${field}.stalemateRepeats`, meta, "a positive integer", (candidate) => candidate >= 1);
|
|
258
|
-
}
|
|
259
195
|
return patch;
|
|
260
196
|
}
|
|
261
197
|
|
|
@@ -296,6 +232,7 @@ function parseChildOverridePatch(value: unknown, field: string, meta: ParseMeta)
|
|
|
296
232
|
if ("enabled" in input) patch.enabled = parseBoolean(input.enabled, `${field}.enabled`, meta);
|
|
297
233
|
if ("model" in input) patch.model = parseNonEmptyString(input.model, `${field}.model`, meta);
|
|
298
234
|
if ("thinking" in input) patch.thinking = parseThinking(input.thinking, `${field}.thinking`, meta);
|
|
235
|
+
if ("cadence" in input) patch.cadence = parseCadencePatch(input.cadence, `${field}.cadence`, meta);
|
|
299
236
|
return patch;
|
|
300
237
|
}
|
|
301
238
|
|
|
@@ -309,7 +246,7 @@ function parseChildrenPatch(value: unknown, field: string, meta: ParseMeta): Wat
|
|
|
309
246
|
if ("watchdogTailTimeoutMs" in input) {
|
|
310
247
|
patch.watchdogTailTimeoutMs = parseInteger(input.watchdogTailTimeoutMs, `${field}.watchdogTailTimeoutMs`, meta, "a positive integer", (candidate) => candidate >= 1);
|
|
311
248
|
}
|
|
312
|
-
if ("
|
|
249
|
+
if ("cadence" in input) patch.cadence = parseCadencePatch(input.cadence, `${field}.cadence`, meta);
|
|
313
250
|
if ("overrides" in input) {
|
|
314
251
|
const overrides = parseObject(input.overrides, `${field}.overrides`, meta);
|
|
315
252
|
patch.overrides = {};
|
|
@@ -321,15 +258,6 @@ function parseChildrenPatch(value: unknown, field: string, meta: ParseMeta): Wat
|
|
|
321
258
|
return patch;
|
|
322
259
|
}
|
|
323
260
|
|
|
324
|
-
function parseAsyncCompletionPatch(value: unknown, field: string, meta: ParseMeta): WatchdogAsyncCompletionPatch {
|
|
325
|
-
const input = parseObject(value, field, meta);
|
|
326
|
-
assertKnownFields(input, ASYNC_COMPLETION_FIELDS, field, meta);
|
|
327
|
-
const patch: WatchdogAsyncCompletionPatch = {};
|
|
328
|
-
if ("enabled" in input) patch.enabled = parseBoolean(input.enabled, `${field}.enabled`, meta);
|
|
329
|
-
if ("autoFollowBlockers" in input) patch.autoFollowBlockers = parseBoolean(input.autoFollowBlockers, `${field}.autoFollowBlockers`, meta);
|
|
330
|
-
return patch;
|
|
331
|
-
}
|
|
332
|
-
|
|
333
261
|
function parseLspPatch(value: unknown, field: string, meta: ParseMeta): WatchdogLspPatch {
|
|
334
262
|
const input = parseObject(value, field, meta);
|
|
335
263
|
assertKnownFields(input, LSP_FIELDS, field, meta);
|
|
@@ -341,20 +269,43 @@ function parseLspPatch(value: unknown, field: string, meta: ParseMeta): Watchdog
|
|
|
341
269
|
return patch;
|
|
342
270
|
}
|
|
343
271
|
|
|
272
|
+
function parseStringList(value: unknown, field: string, meta: ParseMeta): string[] {
|
|
273
|
+
if (!Array.isArray(value) || value.some((entry) => typeof entry !== "string" || !entry.trim())) throw invalid(meta, field, "an array of non-empty strings");
|
|
274
|
+
return value.map((entry: string) => entry.trim());
|
|
275
|
+
}
|
|
276
|
+
|
|
277
|
+
function parseRoleModelRule(value: unknown, field: string, meta: ParseMeta): WatchdogRoleModelRule {
|
|
278
|
+
const input = parseObject(value, field, meta);
|
|
279
|
+
assertKnownFields(input, ROLE_MODEL_RULE_FIELDS, field, meta);
|
|
280
|
+
const rule: WatchdogRoleModelRule = {};
|
|
281
|
+
if ("allow" in input) rule.allow = parseStringList(input.allow, `${field}.allow`, meta);
|
|
282
|
+
if ("deny" in input) rule.deny = parseStringList(input.deny, `${field}.deny`, meta);
|
|
283
|
+
if ("note" in input) rule.note = parseNonEmptyString(input.note, `${field}.note`, meta);
|
|
284
|
+
return rule;
|
|
285
|
+
}
|
|
286
|
+
|
|
287
|
+
function parseRules(value: unknown, field: string, meta: ParseMeta): WatchdogRulesConfig {
|
|
288
|
+
const input = parseObject(value, field, meta);
|
|
289
|
+
assertKnownFields(input, RULES_FIELDS, field, meta);
|
|
290
|
+
const rules: WatchdogRulesConfig = { action: "warn", roleModels: {} };
|
|
291
|
+
if ("action" in input) rules.action = parseEnum(input.action, `${field}.action`, meta, ["warn", "block"] as const);
|
|
292
|
+
if ("roleModels" in input) {
|
|
293
|
+
for (const [agent, rule] of Object.entries(parseObject(input.roleModels, `${field}.roleModels`, meta))) {
|
|
294
|
+
if (!agent.trim()) throw invalid(meta, `${field}.roleModels`, "agent names to be non-empty");
|
|
295
|
+
rules.roleModels[agent] = parseRoleModelRule(rule, `${field}.roleModels.${agent}`, meta);
|
|
296
|
+
}
|
|
297
|
+
}
|
|
298
|
+
return rules;
|
|
299
|
+
}
|
|
300
|
+
|
|
344
301
|
function parseWatchdogPatch(value: unknown, field: string, meta: ParseMeta): WatchdogConfigPatch {
|
|
345
302
|
const input = parseObject(value, field, meta);
|
|
346
303
|
assertKnownFields(input, WATCHDOG_FIELDS, field, meta);
|
|
347
304
|
const patch: WatchdogConfigPatch = {};
|
|
348
305
|
if ("enabled" in input) patch.enabled = parseBoolean(input.enabled, `${field}.enabled`, meta);
|
|
349
|
-
if ("delivery" in input) patch.delivery = parseEnum<WatchdogDeliveryMode>(input.delivery, `${field}.delivery`, meta, WATCHDOG_DELIVERY_MODES);
|
|
350
|
-
if ("showDuringRun" in input) patch.showDuringRun = parseBoolean(input.showDuringRun, `${field}.showDuringRun`, meta);
|
|
351
|
-
if ("syncBacklog" in input) patch.syncBacklog = parseSyncBacklog(input.syncBacklog, `${field}.syncBacklog`, meta);
|
|
352
306
|
if ("agentEndTimeoutMs" in input) {
|
|
353
307
|
patch.agentEndTimeoutMs = parseInteger(input.agentEndTimeoutMs, `${field}.agentEndTimeoutMs`, meta, "a positive integer", (candidate) => candidate >= 1);
|
|
354
308
|
}
|
|
355
|
-
if ("lateWarningPolicy" in input) {
|
|
356
|
-
patch.lateWarningPolicy = parseEnum<WatchdogLateWarningPolicy>(input.lateWarningPolicy, `${field}.lateWarningPolicy`, meta, WATCHDOG_LATE_WARNING_POLICIES);
|
|
357
|
-
}
|
|
358
309
|
if ("severityThreshold" in input) {
|
|
359
310
|
patch.severityThreshold = parseEnum<WatchdogSeverity>(input.severityThreshold, `${field}.severityThreshold`, meta, WATCHDOG_WARNING_SEVERITIES);
|
|
360
311
|
}
|
|
@@ -362,22 +313,15 @@ function parseWatchdogPatch(value: unknown, field: string, meta: ParseMeta): Wat
|
|
|
362
313
|
patch.maxWarnings = parseNullableInteger(input.maxWarnings, `${field}.maxWarnings`, meta, "null or a non-negative integer", (candidate) => candidate >= 0);
|
|
363
314
|
}
|
|
364
315
|
if ("guidance" in input) patch.guidance = parseGuidancePatch(input.guidance, `${field}.guidance`, meta);
|
|
365
|
-
if ("
|
|
316
|
+
if ("stalemateRepeats" in input) {
|
|
317
|
+
patch.stalemateRepeats = parseInteger(input.stalemateRepeats, `${field}.stalemateRepeats`, meta, "a positive integer", (candidate) => candidate >= 1);
|
|
318
|
+
}
|
|
366
319
|
if ("scope" in input) patch.scope = parseScopePatch(input.scope, `${field}.scope`, meta);
|
|
367
320
|
if ("cadence" in input) patch.cadence = parseCadencePatch(input.cadence, `${field}.cadence`, meta);
|
|
368
321
|
if ("main" in input) patch.main = parseEndpointPatch(input.main, `${field}.main`, meta);
|
|
369
322
|
if ("children" in input) patch.children = parseChildrenPatch(input.children, `${field}.children`, meta);
|
|
370
|
-
if ("asyncCompletion" in input) patch.asyncCompletion = parseAsyncCompletionPatch(input.asyncCompletion, `${field}.asyncCompletion`, meta);
|
|
371
323
|
if ("lsp" in input) patch.lsp = parseLspPatch(input.lsp, `${field}.lsp`, meta);
|
|
372
|
-
if ("
|
|
373
|
-
patch.compactAtPercent = parseInteger(input.compactAtPercent, `${field}.compactAtPercent`, meta, "an integer from 50 through 95", (candidate) => candidate >= 50 && candidate <= 95);
|
|
374
|
-
}
|
|
375
|
-
if ("reviewRetryDelayMs" in input) {
|
|
376
|
-
patch.reviewRetryDelayMs = parseInteger(input.reviewRetryDelayMs, `${field}.reviewRetryDelayMs`, meta, "a positive integer", (candidate) => candidate >= 1);
|
|
377
|
-
}
|
|
378
|
-
if ("maxReviewFailures" in input) {
|
|
379
|
-
patch.maxReviewFailures = parseInteger(input.maxReviewFailures, `${field}.maxReviewFailures`, meta, "a positive integer", (candidate) => candidate >= 1);
|
|
380
|
-
}
|
|
324
|
+
if ("rules" in input) patch.rules = parseRules(input.rules, `${field}.rules`, meta);
|
|
381
325
|
return patch;
|
|
382
326
|
}
|
|
383
327
|
|