pi-subagents 0.66.0 → 0.68.0
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 +138 -0
- package/README.md +5 -4
- package/agents/evidence-auditor.md +34 -0
- package/agents/reviewer.md +3 -2
- package/docs/agents.md +43 -15
- package/docs/configuration.md +67 -23
- package/docs/extension-api.md +38 -19
- package/docs/missions.md +10 -2
- package/docs/models.md +11 -79
- package/docs/observability.md +22 -12
- package/docs/standalone-background.md +59 -0
- package/docs/tool-reference.md +33 -20
- package/docs/watchdog.md +39 -10
- package/docs/workflows.md +37 -13
- package/index.ts +5 -2
- package/inspector-runner.mjs +2 -2
- package/package.json +4 -2
- package/prompts/parallel-review.md +1 -1
- package/runner-peer-loader.mjs +24 -0
- package/runner-peer-preload.mjs +32 -0
- package/skills/pi-subagents/SKILL.md +32 -21
- package/skills/pi-subagents/references/constraints-and-recipes.md +3 -2
- package/skills/pi-subagents/references/execution-controls.md +11 -9
- package/skills/pi-subagents/references/management-authoring-rpc.md +0 -1
- package/skills/pi-subagents/references/multi-lane-orchestration.md +1 -1
- package/skills/pi-subagents/references/prompting-and-roles.md +17 -13
- package/skills/pi-subagents/references/review-and-validation.md +3 -3
- package/src/agents/advertised-agent-prompt.ts +34 -3
- package/src/agents/agent-management.ts +57 -58
- package/src/agents/agent-serializer.ts +4 -3
- package/src/agents/agents.ts +190 -71
- package/src/agents/builtin-names.ts +1 -0
- package/src/agents/chain-serializer.ts +5 -0
- package/src/agents/runtime-agent-registry.ts +7 -6
- package/src/api/delegation.ts +4 -0
- package/src/api/preflight.ts +94 -59
- package/src/api/required-child-extensions.ts +6 -0
- package/src/api/shared-types.ts +2 -0
- package/src/extension/config.ts +10 -37
- package/src/extension/fanout-child.ts +66 -4
- package/src/extension/herdr-pi-bridge.ts +160 -0
- package/src/extension/index.ts +62 -39
- package/src/extension/public-execution.ts +7 -5
- package/src/extension/rpc.ts +4 -0
- package/src/extension/schemas.ts +81 -81
- package/src/extension/tool-description.ts +30 -82
- package/src/inspectors/actions.ts +148 -0
- package/src/inspectors/ghostty/actions.ts +74 -0
- package/src/inspectors/ghostty/plugin.ts +17 -0
- package/src/inspectors/herdr/actions.ts +99 -179
- package/src/inspectors/herdr/plugin.ts +20 -0
- package/src/inspectors/herdr/project-panes.ts +1 -1
- package/src/inspectors/{herdr/inspector-runner.ts → inspector-runner.ts} +12 -12
- package/src/inspectors/plugins.ts +8 -0
- package/src/inspectors/{herdr/session-roots-codec.ts → session-roots-codec.ts} +3 -14
- package/src/inspectors/types.ts +51 -0
- package/src/intercom/intercom-bridge.ts +50 -8
- package/src/intercom/native-supervisor-channel.ts +44 -31
- package/src/policy/authority.ts +4 -0
- package/src/profiles/profiles.ts +12 -6
- package/src/runs/background/active-async-capacity.ts +4 -0
- package/src/runs/background/active-run-index.ts +17 -1
- package/src/runs/background/async-execution.ts +348 -176
- package/src/runs/background/async-job-tracker.ts +8 -6
- package/src/runs/background/async-resume.ts +17 -12
- package/src/runs/background/async-status.ts +15 -4
- package/src/runs/background/auto-drain.ts +23 -10
- package/src/runs/background/binary-bootstrap.ts +38 -0
- package/src/runs/background/chain-append.ts +1 -1
- package/src/runs/background/chain-root-attachment.ts +14 -33
- package/src/runs/background/fleet-view.ts +30 -2
- package/src/runs/background/notify.ts +105 -7
- package/src/runs/background/owned-process-tree.ts +29 -2
- package/src/runs/background/result-files.ts +8 -4
- package/src/runs/background/result-watcher.ts +19 -2
- package/src/runs/background/run-child-session.ts +81 -33
- package/src/runs/background/run-status.ts +3 -0
- package/src/runs/background/runner-aliases.ts +12 -33
- package/src/runs/background/runner-child-launch.ts +6 -1
- package/src/runs/background/runner-child-sessions.ts +5 -4
- package/src/runs/background/runner-http-dispatcher.ts +119 -0
- package/src/runs/background/scheduled-runs.ts +51 -18
- package/src/runs/background/stale-run-reconciler.ts +35 -11
- package/src/runs/background/steering.ts +20 -2
- package/src/runs/background/subagent-runner.ts +441 -304
- package/src/runs/background/subagent-wait.ts +176 -28
- package/src/runs/background/wait-completions.ts +75 -27
- package/src/runs/background/wait-subscriptions.ts +9 -3
- package/src/runs/background/wait-tool.ts +5 -3
- package/src/runs/foreground/async-steering-action.ts +18 -7
- package/src/runs/foreground/async-stop-action.ts +93 -3
- package/src/runs/foreground/execution.ts +134 -248
- package/src/runs/foreground/foreground-history.ts +2 -1
- package/src/runs/foreground/prompt-audit.ts +3 -1
- package/src/runs/foreground/subagent-executor.ts +374 -178
- package/src/runs/foreground/workflow-detach-reconcile.ts +2 -0
- package/src/runs/foreground/workflow-foreground-steering.ts +2 -1
- package/src/runs/shared/acceptance.ts +38 -11
- package/src/runs/shared/async-status-projection.ts +127 -33
- package/src/runs/shared/capability-ceiling.ts +2 -0
- package/src/runs/shared/child-hooks.ts +25 -10
- package/src/runs/shared/child-launch-plan.ts +15 -3
- package/src/runs/shared/child-launch.ts +28 -5
- package/src/runs/shared/child-lifecycle.ts +6 -3
- package/src/runs/shared/child-runtime-config.ts +8 -1
- package/src/runs/shared/child-session.ts +127 -52
- package/src/runs/shared/child-tool-plan.ts +142 -11
- package/src/runs/shared/completion-guard.ts +5 -3
- package/src/runs/shared/dynamic-fanout.ts +2 -2
- package/src/runs/shared/effective-system-prompt.ts +33 -0
- package/src/runs/shared/external-cli-contract.ts +11 -1
- package/src/runs/shared/external-cli-preflight.ts +6 -2
- package/src/runs/shared/external-cli-runner.ts +9 -7
- package/src/runs/shared/herdr-connection.ts +134 -0
- package/src/runs/shared/herdr-external-adapters.ts +169 -0
- package/src/runs/shared/herdr-machine.ts +279 -0
- package/src/runs/shared/herdr-pi-protocol.ts +59 -0
- package/src/runs/shared/herdr-placed-run.ts +263 -0
- package/src/runs/shared/llm-intent-arbiter.ts +12 -3
- package/src/runs/shared/model-resolution-diagnostic.ts +76 -0
- package/src/runs/shared/{model-fallback.ts → model-resolution.ts} +22 -235
- package/src/runs/shared/model-scope.ts +1 -1
- package/src/runs/shared/nested-events.ts +11 -2
- package/src/runs/shared/orca-progress-tabs.ts +1 -1
- package/src/runs/shared/parallel-utils.ts +7 -2
- package/src/runs/shared/pi-spawn.ts +10 -0
- package/src/runs/shared/subagent-prompt-runtime.ts +12 -4
- package/src/runs/shared/task-intent.ts +46 -13
- package/src/runs/shared/workflow-async-child-guidance.ts +18 -0
- package/src/runs/shared/worktree-setup-command.ts +27 -4
- package/src/runs/shared/worktree.ts +45 -15
- package/src/shared/child-cache-retention.ts +43 -0
- package/src/shared/fork-context.ts +15 -72
- package/src/shared/launch-contract.ts +68 -8
- package/src/shared/opencode-session-headers.ts +30 -0
- package/src/shared/pruned-fork.ts +1 -1
- package/src/shared/required-child-extensions.ts +81 -0
- package/src/shared/settings.ts +5 -2
- package/src/shared/shortcuts.ts +0 -4
- package/src/shared/types.ts +74 -30
- package/src/slash/delegation-adapters.ts +3 -1
- package/src/slash/delegation-request.ts +14 -0
- package/src/slash/slash-commands.ts +2 -7
- package/src/slash/subagents-admin.ts +24 -13
- package/src/tui/fleet-status.ts +164 -19
- package/src/tui/fleet.ts +16 -14
- package/src/tui/render.ts +168 -37
- package/src/watchdog/child-status.ts +28 -28
- package/src/watchdog/lsp-diagnostics.ts +1 -1
- package/src/watchdog/model-selection.ts +21 -1
- package/src/watchdog/permission-arbiter.ts +3 -1
- package/src/watchdog/register-child.ts +10 -2
- package/src/watchdog/register-main.ts +39 -35
- package/src/watchdog/render.ts +1 -1
- package/src/watchdog/review.ts +123 -74
- package/src/watchdog/rules.ts +1 -1
- package/src/watchdog/runtime.ts +100 -27
- package/src/watchdog/scope.ts +1 -1
- package/src/watchdog/settings.ts +3 -0
- package/src/watchdog/tool-actions.ts +13 -12
- package/src/watchdog/turn-delta.ts +23 -0
- package/src/watchdog/types.ts +5 -3
- package/src/watchdog/warning-format.ts +1 -1
- package/src/workflows/scripted-workflow.ts +279 -10
- package/src/workflows/workflow-checklist.ts +2 -2
- package/src/workflows/workflow-receipt.ts +21 -3
- package/src/workflows/workflow-resources.ts +13 -2
- package/runner-server-preload.mjs +0 -13
- package/src/runs/shared/model-exclusions.ts +0 -374
- package/src/runs/shared/readonly-model-continuation.ts +0 -69
- package/src/runs/shared/readonly-session-evidence.ts +0 -307
- /package/src/inspectors/{herdr/shell-command.ts → shell-command.ts} +0 -0
package/src/watchdog/runtime.ts
CHANGED
|
@@ -12,8 +12,9 @@ import {
|
|
|
12
12
|
import { ruleViolationWarning, type WatchdogRuleViolation } from "./rules.ts";
|
|
13
13
|
import { WatchdogScopeArtifact } from "./scope.ts";
|
|
14
14
|
import { resolveWatchdogConfig } from "./settings.ts";
|
|
15
|
-
import { formatWatchdogTurnDelta } from "./turn-delta.ts";
|
|
15
|
+
import { formatWatchdogOrchestrationActivity, formatWatchdogTurnDelta } from "./turn-delta.ts";
|
|
16
16
|
import {
|
|
17
|
+
WATCHDOG_WARNING_IMPORTANCES,
|
|
17
18
|
type ResolvedWatchdogConfig,
|
|
18
19
|
type WatchdogEndpointConfig,
|
|
19
20
|
type WatchdogLspRuntimeSnapshot,
|
|
@@ -31,6 +32,9 @@ type ReviewStopReason = "stop" | "error" | "aborted" | "length";
|
|
|
31
32
|
export interface WatchdogReviewResult {
|
|
32
33
|
warnings?: WatchdogWarning[];
|
|
33
34
|
stopReason?: ReviewStopReason;
|
|
35
|
+
/** Provider error text for a failed review, surfaced in status `Last error`. */
|
|
36
|
+
errorMessage?: string;
|
|
37
|
+
clarification?: { question: string; evidence: string };
|
|
34
38
|
}
|
|
35
39
|
|
|
36
40
|
export interface WatchdogReviewRequest {
|
|
@@ -40,6 +44,7 @@ export interface WatchdogReviewRequest {
|
|
|
40
44
|
reviewId: number;
|
|
41
45
|
config: ResolvedWatchdogConfig;
|
|
42
46
|
emitWarning(warning: WatchdogWarning): boolean;
|
|
47
|
+
allowClarification?: boolean;
|
|
43
48
|
signal?: AbortSignal;
|
|
44
49
|
}
|
|
45
50
|
|
|
@@ -81,6 +86,9 @@ interface MainWatchdogRuntimeOptions {
|
|
|
81
86
|
review?: WatchdogReviewFunction;
|
|
82
87
|
reviewDescription?: string;
|
|
83
88
|
displayWarning?: (warning: WatchdogWarningDetails, options?: WatchdogWarningSendOptions) => void;
|
|
89
|
+
displayUserWarning?: (warning: WatchdogWarningDetails) => void;
|
|
90
|
+
/** Supplying this main-session delivery capability gates clarification (children omit it). */
|
|
91
|
+
displayClarification?: (content: string) => void;
|
|
84
92
|
reviewChangesOnly?: boolean;
|
|
85
93
|
lspDiagnostics?: WatchdogLspDiagnosticsFunction;
|
|
86
94
|
repoChangeSignature?: typeof computeWatchdogRepoChangeSignature;
|
|
@@ -88,8 +96,8 @@ interface MainWatchdogRuntimeOptions {
|
|
|
88
96
|
|
|
89
97
|
export type WatchdogWarningSendOptions = { deliverAs: "steer" } | { triggerTurn: false };
|
|
90
98
|
|
|
91
|
-
type ContextLike = Pick<ExtensionContext, "cwd"
|
|
92
|
-
type ReviewDeltaOutcome = "completed" | "timeout" | "stale"
|
|
99
|
+
type ContextLike = Pick<ExtensionContext, "cwd"> & { signal?: AbortSignal };
|
|
100
|
+
type ReviewDeltaOutcome = "completed" | "timeout" | "stale" | NonNullable<WatchdogReviewResult["clarification"]>;
|
|
93
101
|
|
|
94
102
|
const DEFAULT_REVIEW: WatchdogReviewFunction = () => ({ warnings: [] });
|
|
95
103
|
const MAX_REVIEW_INPUT_CHARS = 24_000;
|
|
@@ -126,6 +134,7 @@ export class MainWatchdogRuntime {
|
|
|
126
134
|
private readonly reviewConnected: boolean;
|
|
127
135
|
private readonly reviewDescription: string;
|
|
128
136
|
private readonly displayWarning: ((warning: WatchdogWarningDetails, options?: WatchdogWarningSendOptions) => void) | undefined;
|
|
137
|
+
private readonly displayUserWarning: ((warning: WatchdogWarningDetails) => void) | undefined;
|
|
129
138
|
private readonly reviewChangesOnly: boolean;
|
|
130
139
|
private readonly lspDiagnostics: WatchdogLspDiagnosticsFunction;
|
|
131
140
|
private readonly repoChangeSignature: typeof computeWatchdogRepoChangeSignature;
|
|
@@ -170,6 +179,11 @@ export class MainWatchdogRuntime {
|
|
|
170
179
|
private activeReviewAbortController: AbortController | undefined;
|
|
171
180
|
private failedReviews = 0;
|
|
172
181
|
private staleReviews = 0;
|
|
182
|
+
private readonly displayClarification: MainWatchdogRuntimeOptions["displayClarification"];
|
|
183
|
+
private askedThisPrompt = false;
|
|
184
|
+
private activityTail = "";
|
|
185
|
+
private activityPending = false;
|
|
186
|
+
private activityReviewUsed = false;
|
|
173
187
|
|
|
174
188
|
constructor(options: MainWatchdogRuntimeOptions = {}) {
|
|
175
189
|
this.cwd = options.cwd ?? process.cwd();
|
|
@@ -178,6 +192,8 @@ export class MainWatchdogRuntime {
|
|
|
178
192
|
this.reviewConnected = Boolean(options.review);
|
|
179
193
|
this.reviewDescription = options.reviewDescription ?? (options.review ? "injected seam" : "not wired");
|
|
180
194
|
this.displayWarning = options.displayWarning;
|
|
195
|
+
this.displayUserWarning = options.displayUserWarning;
|
|
196
|
+
this.displayClarification = options.displayClarification;
|
|
181
197
|
this.reviewChangesOnly = options.reviewChangesOnly === true;
|
|
182
198
|
this.lspDiagnostics = options.lspDiagnostics ?? collectWatchdogLspDiagnostics;
|
|
183
199
|
this.repoChangeSignature = options.repoChangeSignature ?? computeWatchdogRepoChangeSignature;
|
|
@@ -200,6 +216,8 @@ export class MainWatchdogRuntime {
|
|
|
200
216
|
refreshConfig(cwd = this.cwd): WatchdogSettingsResult {
|
|
201
217
|
this.cwd = cwd;
|
|
202
218
|
const wasEnabled = this.isEnabled();
|
|
219
|
+
const hadClarification = this.configResult.config.clarification;
|
|
220
|
+
const previousMain = this.configResult.config.main;
|
|
203
221
|
const session = this.sessionOverrideEnabled === undefined && this.sessionModelOverride === undefined
|
|
204
222
|
? undefined
|
|
205
223
|
: {
|
|
@@ -215,6 +233,10 @@ export class MainWatchdogRuntime {
|
|
|
215
233
|
this.guard = new WatchdogEmissionGuard({ maxWarnings: this.guardMaxWarnings });
|
|
216
234
|
}
|
|
217
235
|
if (wasEnabled && !this.isEnabled()) this.invalidateActiveReview("watchdog disabled");
|
|
236
|
+
if (!this.isEnabled() || !this.configResult.config.clarification) this.clearActivity();
|
|
237
|
+
if (this.displayClarification && (hadClarification || this.configResult.config.clarification) && (this.reviewing || this.waitingAtAgentEnd)) {
|
|
238
|
+
if (!this.configResult.config.clarification || previousMain.model !== this.configResult.config.main.model || previousMain.thinking !== this.configResult.config.main.thinking) this.invalidateActiveReview("clarification configuration changed");
|
|
239
|
+
}
|
|
218
240
|
return this.configResult;
|
|
219
241
|
}
|
|
220
242
|
|
|
@@ -252,7 +274,8 @@ export class MainWatchdogRuntime {
|
|
|
252
274
|
return this.getSnapshot();
|
|
253
275
|
}
|
|
254
276
|
|
|
255
|
-
reset(_reason = "reset", options: { clearReviewInputSignature?: boolean; resetChangeSignature?: boolean; clearLspLedger?: boolean; clearScope?: boolean } = {}): void {
|
|
277
|
+
reset(_reason = "reset", options: { clearReviewInputSignature?: boolean; resetChangeSignature?: boolean; clearLspLedger?: boolean; clearScope?: boolean; clearActivity?: boolean } = {}): void {
|
|
278
|
+
this.activeReviewAbortController?.abort();
|
|
256
279
|
this.abortActiveAgentEnd();
|
|
257
280
|
this.epoch++;
|
|
258
281
|
this.status = "idle";
|
|
@@ -274,6 +297,7 @@ export class MainWatchdogRuntime {
|
|
|
274
297
|
this.lastLspSnapshot = undefined;
|
|
275
298
|
}
|
|
276
299
|
if (options.clearScope) this.scope.reset();
|
|
300
|
+
if (options.clearScope || options.clearActivity) this.clearActivity();
|
|
277
301
|
if (options.clearReviewInputSignature) this.lastReviewInputSignature = undefined;
|
|
278
302
|
if (options.resetChangeSignature) this.resetRepoChangeBaseline({ reviewed: true });
|
|
279
303
|
this.guard.reset();
|
|
@@ -281,6 +305,8 @@ export class MainWatchdogRuntime {
|
|
|
281
305
|
}
|
|
282
306
|
|
|
283
307
|
dispose(): void {
|
|
308
|
+
this.clearActivity();
|
|
309
|
+
this.activeReviewAbortController?.abort();
|
|
284
310
|
this.disposed = true;
|
|
285
311
|
this.abortActiveAgentEnd();
|
|
286
312
|
this.epoch++;
|
|
@@ -305,6 +331,8 @@ export class MainWatchdogRuntime {
|
|
|
305
331
|
if (this.disposed) return;
|
|
306
332
|
const incomingPrompt = promptFromBeforeAgentStart(event);
|
|
307
333
|
this.reset("before_agent_start");
|
|
334
|
+
this.askedThisPrompt = false;
|
|
335
|
+
this.activityReviewUsed = false;
|
|
308
336
|
this.resetBoundaryRepeats();
|
|
309
337
|
this.refreshConfig(ctx.cwd);
|
|
310
338
|
this.userPrompt = incomingPrompt;
|
|
@@ -322,6 +350,13 @@ export class MainWatchdogRuntime {
|
|
|
322
350
|
this.refreshConfig(ctx.cwd);
|
|
323
351
|
if (!this.isEnabled()) return;
|
|
324
352
|
try {
|
|
353
|
+
if (this.displayClarification && this.configResult.config.clarification && this.boundaryRepeats === 0) {
|
|
354
|
+
const activity = formatWatchdogOrchestrationActivity(event);
|
|
355
|
+
if (activity) {
|
|
356
|
+
this.activityTail = [this.activityTail, boundWatchdogReviewText(activity, 3_000)].filter(Boolean).join(REVIEW_DELTA_SEPARATOR).slice(-6_000);
|
|
357
|
+
this.activityPending = true;
|
|
358
|
+
}
|
|
359
|
+
}
|
|
325
360
|
this.observedRepoEditThisTurn ||= eventIndicatesRepoEdit(event);
|
|
326
361
|
const delta = formatWatchdogTurnDelta({
|
|
327
362
|
includeUserPrompt: this.includeUserPromptInNextDelta,
|
|
@@ -359,20 +394,19 @@ export class MainWatchdogRuntime {
|
|
|
359
394
|
if (this.disposed) return;
|
|
360
395
|
this.refreshConfig(ctx.cwd);
|
|
361
396
|
if (!this.isEnabled()) return;
|
|
362
|
-
|
|
363
|
-
|
|
364
|
-
|
|
365
|
-
|
|
366
|
-
|
|
367
|
-
return;
|
|
368
|
-
}
|
|
369
|
-
if (changeSignature && changeSignature.key === this.lastReviewedChangeSignature) {
|
|
397
|
+
if (ctx.signal?.aborted) return;
|
|
398
|
+
const changeSignature = this.resolveReviewChangeSignature(this.currentRepoChangeSignature(ctx.cwd), ctx.cwd);
|
|
399
|
+
const activityReview = Boolean(this.displayClarification && this.configResult.config.clarification && !this.activityReviewUsed && this.boundaryRepeats === 0 && this.activityPending);
|
|
400
|
+
const knownEvidence = changeSignature && changeSignature.key === this.lastReviewedChangeSignature;
|
|
401
|
+
if (!activityReview && (knownEvidence || (this.reviewChangesOnly && !changeSignature))) {
|
|
370
402
|
this.clearPendingDeltas();
|
|
371
|
-
this.status = "idle";
|
|
403
|
+
if (knownEvidence || this.status === "queued") this.status = "idle";
|
|
372
404
|
this.resolveWaiters(true);
|
|
373
405
|
return;
|
|
374
406
|
}
|
|
375
407
|
this.cancelMidRunReview();
|
|
408
|
+
if (activityReview && (!changeSignature || knownEvidence)) this.activityReviewUsed = true;
|
|
409
|
+
this.activityPending = false;
|
|
376
410
|
this.waitingAtAgentEnd = true;
|
|
377
411
|
const agentEndEpoch = this.epoch;
|
|
378
412
|
const agentEndId = ++this.agentEndIdCounter;
|
|
@@ -403,7 +437,8 @@ export class MainWatchdogRuntime {
|
|
|
403
437
|
this.resolveWaiters(true);
|
|
404
438
|
return;
|
|
405
439
|
}
|
|
406
|
-
const outcome = await this.reviewDelta(delta, this.configResult.config.agentEndTimeoutMs);
|
|
440
|
+
const outcome = await this.reviewDelta(delta, this.configResult.config.agentEndTimeoutMs, { allowClarification: true });
|
|
441
|
+
if (!this.isAgentEndCurrent(agentEndEpoch, agentEndId)) return;
|
|
407
442
|
this.waitingAtAgentEnd = false;
|
|
408
443
|
if (outcome === "timeout") {
|
|
409
444
|
this.staleReviews++;
|
|
@@ -413,11 +448,16 @@ export class MainWatchdogRuntime {
|
|
|
413
448
|
this.resolveWaiters(true);
|
|
414
449
|
return;
|
|
415
450
|
}
|
|
416
|
-
if (outcome === "completed" && this.status !== "failed" && this.status !== "stale") {
|
|
451
|
+
if ((outcome === "completed" || typeof outcome === "object") && this.status !== "failed" && this.status !== "stale") {
|
|
452
|
+
// A question consumes this evidence just like a completed review, not a pending exchange.
|
|
417
453
|
this.lastReviewInputSignature = signature;
|
|
418
454
|
if (changeSignature) this.lastReviewedChangeSignature = changeSignature.key;
|
|
419
455
|
this.currentChangedPaths = changeSignature?.changedPaths;
|
|
420
456
|
this.status = "idle";
|
|
457
|
+
if (typeof outcome === "object" && !ctx.signal?.aborted) this.displayClarification?.([
|
|
458
|
+
"Main watchdog clarification:", outcome.question, `Evidence: ${outcome.evidence}`,
|
|
459
|
+
"Consider this missing context as you continue the task. This is not approval, permission, or a warning. The reviewer has yielded; no reply or follow-up review is required.",
|
|
460
|
+
].join("\n"));
|
|
421
461
|
}
|
|
422
462
|
this.resolveWaiters(true);
|
|
423
463
|
} finally {
|
|
@@ -431,13 +471,13 @@ export class MainWatchdogRuntime {
|
|
|
431
471
|
this.ruleWarningsThisRun.add(violation.summary);
|
|
432
472
|
const details = normalizeWatchdogWarningDetails(ruleViolationWarning(violation), { state: "displayed", displayedAt: new Date().toISOString() });
|
|
433
473
|
this.lastWarning = details;
|
|
434
|
-
this.
|
|
474
|
+
this.routeWarning(details, { deliverAs: "steer" });
|
|
435
475
|
}
|
|
436
476
|
|
|
437
|
-
|
|
477
|
+
displayRecordedWarning(warning: WatchdogWarning): void {
|
|
438
478
|
const details = normalizeWatchdogWarningDetails(warning, { state: "displayed", source: warning.source ?? "main" });
|
|
439
479
|
this.lastWarning = details;
|
|
440
|
-
|
|
480
|
+
this.routeWarning(details);
|
|
441
481
|
}
|
|
442
482
|
|
|
443
483
|
getSnapshot(cwd?: string): WatchdogRuntimeSnapshot {
|
|
@@ -468,6 +508,15 @@ export class MainWatchdogRuntime {
|
|
|
468
508
|
};
|
|
469
509
|
}
|
|
470
510
|
|
|
511
|
+
/** A real mid-stream user input can steer without emitting before_agent_start. */
|
|
512
|
+
handleUserInput(): void {
|
|
513
|
+
if (this.displayClarification && this.configResult.config.clarification && (this.reviewing || this.waitingAtAgentEnd)) this.reset("new user input");
|
|
514
|
+
}
|
|
515
|
+
|
|
516
|
+
handleModelChange(): void {
|
|
517
|
+
if (this.displayClarification && this.configResult.config.clarification && (this.reviewing || this.waitingAtAgentEnd)) this.reset("model changed");
|
|
518
|
+
}
|
|
519
|
+
|
|
471
520
|
async waitForIdle(timeoutMs = 1_000): Promise<boolean> {
|
|
472
521
|
return this.waitForSettled(timeoutMs);
|
|
473
522
|
}
|
|
@@ -491,7 +540,13 @@ export class MainWatchdogRuntime {
|
|
|
491
540
|
}
|
|
492
541
|
|
|
493
542
|
private warningMeetsThreshold(warning: WatchdogWarning): boolean {
|
|
494
|
-
return
|
|
543
|
+
return (WATCHDOG_WARNING_IMPORTANCES as readonly string[]).includes(warning.importance)
|
|
544
|
+
&& (this.configResult.config.severityThreshold === "concern" || warning.severity === "blocker");
|
|
545
|
+
}
|
|
546
|
+
|
|
547
|
+
private routeWarning(details: WatchdogWarningDetails, options?: WatchdogWarningSendOptions): void {
|
|
548
|
+
if (details.importance === "high") this.displayWarning?.(details, options);
|
|
549
|
+
else this.displayUserWarning?.(details);
|
|
495
550
|
}
|
|
496
551
|
|
|
497
552
|
private acceptWarning(epoch: number, reviewId: number, warning: WatchdogWarning): boolean {
|
|
@@ -523,6 +578,7 @@ export class MainWatchdogRuntime {
|
|
|
523
578
|
}
|
|
524
579
|
|
|
525
580
|
private invalidateActiveReview(_reason: string): void {
|
|
581
|
+
this.activeReviewAbortController?.abort();
|
|
526
582
|
this.abortActiveAgentEnd();
|
|
527
583
|
this.epoch++;
|
|
528
584
|
this.status = "idle";
|
|
@@ -567,7 +623,7 @@ export class MainWatchdogRuntime {
|
|
|
567
623
|
this.activeReviewWarning = undefined;
|
|
568
624
|
}
|
|
569
625
|
|
|
570
|
-
private async reviewDelta(delta: string, timeoutMs: number, options: { correction?: boolean } = {}): Promise<ReviewDeltaOutcome> {
|
|
626
|
+
private async reviewDelta(delta: string, timeoutMs: number, options: { correction?: boolean; allowClarification?: boolean } = {}): Promise<ReviewDeltaOutcome> {
|
|
571
627
|
if (this.reviewing || this.disposed) return "stale";
|
|
572
628
|
this.reviewing = true;
|
|
573
629
|
const reviewEpoch = this.epoch;
|
|
@@ -578,6 +634,7 @@ export class MainWatchdogRuntime {
|
|
|
578
634
|
let timeout: ReturnType<typeof setTimeout> | undefined;
|
|
579
635
|
const abortController = new AbortController();
|
|
580
636
|
this.activeReviewAbortController = abortController;
|
|
637
|
+
const allowClarification = Boolean(options.allowClarification && this.displayClarification && this.configResult.config.clarification && !this.askedThisPrompt && !this.stalemate);
|
|
581
638
|
const reviewPromise = Promise.resolve().then(() => this.review({
|
|
582
639
|
delta,
|
|
583
640
|
epoch: reviewEpoch,
|
|
@@ -586,6 +643,7 @@ export class MainWatchdogRuntime {
|
|
|
586
643
|
hasScope: this.scopeBlock().trim().length > 0,
|
|
587
644
|
signal: abortController.signal,
|
|
588
645
|
emitWarning: (warning) => this.acceptWarning(reviewEpoch, reviewId, warning),
|
|
646
|
+
...(allowClarification ? { allowClarification: true } : {}),
|
|
589
647
|
}));
|
|
590
648
|
try {
|
|
591
649
|
const result = await Promise.race([
|
|
@@ -600,9 +658,18 @@ export class MainWatchdogRuntime {
|
|
|
600
658
|
}
|
|
601
659
|
if (!this.isCurrent(reviewEpoch, reviewId)) return "stale";
|
|
602
660
|
if (!result) return "stale";
|
|
661
|
+
if (allowClarification && this.configResult.config.clarification && result.clarification && (!result.stopReason || result.stopReason === "stop") && !this.activeReviewWarning && !result.warnings?.length && !abortController.signal.aborted) {
|
|
662
|
+
const question = result.clarification.question.trim();
|
|
663
|
+
const evidence = result.clarification.evidence.trim();
|
|
664
|
+
if (question && evidence) {
|
|
665
|
+
this.askedThisPrompt = true;
|
|
666
|
+
return { question: boundWatchdogReviewText(question, 1_000), evidence: boundWatchdogReviewText(evidence, 2_000) };
|
|
667
|
+
}
|
|
668
|
+
}
|
|
603
669
|
for (const warning of result.warnings ?? []) this.acceptWarning(reviewEpoch, reviewId, warning);
|
|
604
670
|
if (result.stopReason && result.stopReason !== "stop") {
|
|
605
|
-
|
|
671
|
+
const detail = result.errorMessage?.trim() ? ` ${boundWatchdogReviewText(result.errorMessage.trim(), 600)}` : "";
|
|
672
|
+
this.fail(`Watchdog review ended with stop reason '${result.stopReason}'.${detail}`);
|
|
606
673
|
return "completed";
|
|
607
674
|
}
|
|
608
675
|
this.displayAcceptedReviewWarning(options.correction);
|
|
@@ -634,7 +701,7 @@ export class MainWatchdogRuntime {
|
|
|
634
701
|
};
|
|
635
702
|
if (correction) {
|
|
636
703
|
this.lastWarning = details;
|
|
637
|
-
this.
|
|
704
|
+
this.routeWarning(details, { deliverAs: "steer" });
|
|
638
705
|
return;
|
|
639
706
|
}
|
|
640
707
|
this.deliverBoundaryWarning(details);
|
|
@@ -655,7 +722,7 @@ export class MainWatchdogRuntime {
|
|
|
655
722
|
: details;
|
|
656
723
|
this.stalemate = stalemate;
|
|
657
724
|
this.lastWarning = delivered;
|
|
658
|
-
this.
|
|
725
|
+
this.routeWarning(delivered, stalemate ? { triggerTurn: false } : undefined);
|
|
659
726
|
}
|
|
660
727
|
|
|
661
728
|
// The previous boundary finding is a repeat to count, not a duplicate, until stalemate.
|
|
@@ -681,9 +748,8 @@ export class MainWatchdogRuntime {
|
|
|
681
748
|
this.observedRepoEditThisTurn = false;
|
|
682
749
|
}
|
|
683
750
|
|
|
684
|
-
private resolveReviewChangeSignature(
|
|
751
|
+
private resolveReviewChangeSignature(current: WatchdogRepoChangeSignature | undefined, cwd: string): WatchdogRepoChangeSignature | undefined {
|
|
685
752
|
if (!this.reviewChangesOnly) return undefined;
|
|
686
|
-
const current = this.currentRepoChangeSignature(cwd);
|
|
687
753
|
if (current) {
|
|
688
754
|
this.currentChangedPaths = current.changedPaths;
|
|
689
755
|
if (current.key === this.turnStartChangeSignature?.key) return undefined;
|
|
@@ -785,13 +851,14 @@ export class MainWatchdogRuntime {
|
|
|
785
851
|
const changes = changeSignature?.changedPaths.length
|
|
786
852
|
? ["Changed repo paths:", ...changeSignature.changedPaths.slice(0, 200).map((file) => `- ${file}`)].join("\n")
|
|
787
853
|
: "";
|
|
788
|
-
const
|
|
854
|
+
const activity = this.activityTail ? `Recent delivered orchestration activity (oldest first; bounded observations, not a task board; waits/holds are not evidence of neglect):\n${this.activityTail}` : "";
|
|
855
|
+
const contextPieces = [scopeBlock, changes, lspBlock, activity].filter(Boolean);
|
|
789
856
|
if (!contextPieces.length) return boundWatchdogReviewText(input);
|
|
790
857
|
|
|
791
858
|
const maxContextLength = Math.floor(MAX_REVIEW_INPUT_CHARS / 2);
|
|
792
859
|
const maxPieceLength = Math.max(1_000, Math.floor(maxContextLength / contextPieces.length));
|
|
793
860
|
const boundedContext = contextPieces.map((piece) => piece.length > maxPieceLength
|
|
794
|
-
? `${piece.slice(0, maxPieceLength - 6)}\n- ...`
|
|
861
|
+
? piece === activity ? boundWatchdogReviewText(piece, maxPieceLength) : `${piece.slice(0, maxPieceLength - 6)}\n- ...`
|
|
795
862
|
: piece).join(REVIEW_DELTA_SEPARATOR);
|
|
796
863
|
const separatorLength = input ? REVIEW_DELTA_SEPARATOR.length : 0;
|
|
797
864
|
const inputBudget = MAX_REVIEW_INPUT_CHARS - boundedContext.length - separatorLength;
|
|
@@ -808,6 +875,12 @@ export class MainWatchdogRuntime {
|
|
|
808
875
|
this.pendingDeltaChars = 0;
|
|
809
876
|
}
|
|
810
877
|
|
|
878
|
+
private clearActivity(): void {
|
|
879
|
+
if (!this.activityTail) return;
|
|
880
|
+
this.activityTail = "";
|
|
881
|
+
this.activityPending = false;
|
|
882
|
+
}
|
|
883
|
+
|
|
811
884
|
private fail(message: string): void {
|
|
812
885
|
this.failedReviews++;
|
|
813
886
|
this.lastError = message;
|
package/src/watchdog/scope.ts
CHANGED
|
@@ -32,7 +32,7 @@ export class WatchdogScopeArtifact {
|
|
|
32
32
|
if (!this.entries.length) return "";
|
|
33
33
|
return [
|
|
34
34
|
"Current scope:",
|
|
35
|
-
"The following real user prompts are the current scope record, newest last.
|
|
35
|
+
"The following real user prompts are the current scope record, newest last. Side questions are additive, not scope drift or cancellation of older objectives; only explicit changes supersede requirements. Use watchdog_warn for evidence-backed reminders of forgotten authorized work, not dependencies still pending or explicit holds. The orchestrator owns task tracking. Flag unauthorized work as category 'scope-drift'.",
|
|
36
36
|
...this.entries.map((entry, index) => [
|
|
37
37
|
`Scope prompt ${index + 1} (${entry.createdAt}):`,
|
|
38
38
|
entry.prompt,
|
package/src/watchdog/settings.ts
CHANGED
|
@@ -61,6 +61,7 @@ interface ParseMeta {
|
|
|
61
61
|
|
|
62
62
|
export const DEFAULT_WATCHDOG_CONFIG: ResolvedWatchdogConfig = {
|
|
63
63
|
enabled: false,
|
|
64
|
+
clarification: false,
|
|
64
65
|
agentEndTimeoutMs: 30_000,
|
|
65
66
|
severityThreshold: "concern",
|
|
66
67
|
maxWarnings: null,
|
|
@@ -92,6 +93,7 @@ export const DEFAULT_WATCHDOG_CONFIG: ResolvedWatchdogConfig = {
|
|
|
92
93
|
|
|
93
94
|
const WATCHDOG_FIELDS = new Set([
|
|
94
95
|
"enabled",
|
|
96
|
+
"clarification",
|
|
95
97
|
"agentEndTimeoutMs",
|
|
96
98
|
"severityThreshold",
|
|
97
99
|
"maxWarnings",
|
|
@@ -303,6 +305,7 @@ function parseWatchdogPatch(value: unknown, field: string, meta: ParseMeta): Wat
|
|
|
303
305
|
assertKnownFields(input, WATCHDOG_FIELDS, field, meta);
|
|
304
306
|
const patch: WatchdogConfigPatch = {};
|
|
305
307
|
if ("enabled" in input) patch.enabled = parseBoolean(input.enabled, `${field}.enabled`, meta);
|
|
308
|
+
if ("clarification" in input) patch.clarification = parseBoolean(input.clarification, `${field}.clarification`, meta);
|
|
306
309
|
if ("agentEndTimeoutMs" in input) {
|
|
307
310
|
patch.agentEndTimeoutMs = parseInteger(input.agentEndTimeoutMs, `${field}.agentEndTimeoutMs`, meta, "a positive integer", (candidate) => candidate >= 1);
|
|
308
311
|
}
|
|
@@ -4,8 +4,8 @@ import { THINKING_LEVELS, type ThinkingLevel } from "../shared/model-info.ts";
|
|
|
4
4
|
import type { Details } from "../shared/types.ts";
|
|
5
5
|
import { buildWatchdogStatus } from "./register-main.ts";
|
|
6
6
|
import type { MainWatchdogRuntime } from "./runtime.ts";
|
|
7
|
-
import { parseWatchdogThinkingInput, recommendStrongWatchdogModel, resolveWatchdogModelInput } from "./model-selection.ts";
|
|
8
|
-
import { writeWatchdogModelSettings, type WatchdogModelSettingsTarget, type WatchdogSettingsWriteScope } from "./settings.ts";
|
|
7
|
+
import { formatWatchdogRecommendation, parseWatchdogThinkingInput, recommendStrongWatchdogModel, recommendWatchdogModel, resolveWatchdogModelInput } from "./model-selection.ts";
|
|
8
|
+
import { resolveWatchdogConfig, writeWatchdogModelSettings, type WatchdogModelSettingsTarget, type WatchdogSettingsWriteScope } from "./settings.ts";
|
|
9
9
|
|
|
10
10
|
interface WatchdogToolParams {
|
|
11
11
|
action?: string;
|
|
@@ -52,7 +52,7 @@ function parseThinking(raw: string | false | undefined): ThinkingLevel | false |
|
|
|
52
52
|
return parseWatchdogThinkingInput(raw, "watchdog.configure thinking") ?? undefined;
|
|
53
53
|
}
|
|
54
54
|
|
|
55
|
-
function resolveConfiguredValue(ctx: ExtensionContext, params: WatchdogToolParams): { model?: string | null; thinking?: ThinkingLevel | false | null; description: string } {
|
|
55
|
+
function resolveConfiguredValue(ctx: ExtensionContext, params: WatchdogToolParams, runtime?: MainWatchdogRuntime): { model?: string | null; thinking?: ThinkingLevel | false | null; description: string } {
|
|
56
56
|
const thinking = parseThinking(params.thinking);
|
|
57
57
|
const rawModel = params.model?.trim();
|
|
58
58
|
if (!rawModel) {
|
|
@@ -61,7 +61,9 @@ function resolveConfiguredValue(ctx: ExtensionContext, params: WatchdogToolParam
|
|
|
61
61
|
}
|
|
62
62
|
if (rawModel === "inherit") return { model: null, thinking: thinking ?? null, description: "inherit" };
|
|
63
63
|
if (rawModel === "recommended") {
|
|
64
|
-
const recommendation =
|
|
64
|
+
const recommendation = (params.target ?? "main") === "main"
|
|
65
|
+
? recommendWatchdogModel(ctx, (runtime?.getSnapshot(ctx.cwd).config ?? resolveWatchdogConfig(ctx.cwd).config).main)
|
|
66
|
+
: recommendStrongWatchdogModel(ctx);
|
|
65
67
|
return {
|
|
66
68
|
model: recommendation.model,
|
|
67
69
|
thinking: recommendation.thinking,
|
|
@@ -76,11 +78,11 @@ function resolveConfiguredValue(ctx: ExtensionContext, params: WatchdogToolParam
|
|
|
76
78
|
};
|
|
77
79
|
}
|
|
78
80
|
|
|
79
|
-
function buildRecommendationText(ctx: ExtensionContext): string {
|
|
80
|
-
const recommendation =
|
|
81
|
+
function buildRecommendationText(ctx: ExtensionContext, runtime?: MainWatchdogRuntime): string {
|
|
82
|
+
const recommendation = recommendWatchdogModel(ctx, (runtime?.getSnapshot(ctx.cwd).config ?? resolveWatchdogConfig(ctx.cwd).config).main);
|
|
81
83
|
return [
|
|
82
84
|
"Subagent watchdog recommended model",
|
|
83
|
-
`Recommended: ${recommendation.model}:${recommendation.thinking}`,
|
|
85
|
+
recommendation.source === "configured" ? formatWatchdogRecommendation(recommendation) : `Recommended: ${recommendation.model}:${recommendation.thinking}`,
|
|
84
86
|
`Reason: ${recommendation.reason}`,
|
|
85
87
|
"Apply temporarily with subagent({ action: \"watchdog.configure\", scope: \"session\", model: \"recommended\" }).",
|
|
86
88
|
"Persist with scope: \"project\" or scope: \"user\" only when the user asks for that scope.",
|
|
@@ -100,10 +102,9 @@ function buildCheckText(runtime: MainWatchdogRuntime | undefined, ctx: Extension
|
|
|
100
102
|
}
|
|
101
103
|
lines.push(`LSP diagnostics: ${snapshot.lsp.enabled ? "on" : "off"} · ${snapshot.lsp.status}`);
|
|
102
104
|
try {
|
|
103
|
-
|
|
104
|
-
lines.push(`Recommended strong watchdog: ${recommendation.model}:${recommendation.thinking}`);
|
|
105
|
+
lines.push(formatWatchdogRecommendation(recommendWatchdogModel(ctx, snapshot.config.main)));
|
|
105
106
|
} catch (error) {
|
|
106
|
-
lines.push(`
|
|
107
|
+
lines.push(`Watchdog recommendation unavailable: ${messageFromError(error)}`);
|
|
107
108
|
}
|
|
108
109
|
return lines.join("\n");
|
|
109
110
|
}
|
|
@@ -114,13 +115,13 @@ export function handleWatchdogToolAction(action: string, params: WatchdogToolPar
|
|
|
114
115
|
if (!runtime) return result("Subagent watchdog runtime is unavailable.", true);
|
|
115
116
|
return result(buildWatchdogStatus(runtime.getSnapshot(ctx.cwd), ctx));
|
|
116
117
|
}
|
|
117
|
-
if (action === "watchdog.recommend-model") return result(buildRecommendationText(ctx));
|
|
118
|
+
if (action === "watchdog.recommend-model") return result(buildRecommendationText(ctx, runtime));
|
|
118
119
|
if (action === "watchdog.check") return result(buildCheckText(runtime, ctx));
|
|
119
120
|
if (action !== "watchdog.configure") return result(`Unknown watchdog action: ${action}`, true);
|
|
120
121
|
|
|
121
122
|
const scope = parseScope(params.scope);
|
|
122
123
|
const target = parseTarget(params);
|
|
123
|
-
const value = resolveConfiguredValue(ctx, params);
|
|
124
|
+
const value = resolveConfiguredValue(ctx, params, runtime);
|
|
124
125
|
if (scope === "session") {
|
|
125
126
|
if (!runtime) return result("Subagent watchdog runtime is unavailable.", true);
|
|
126
127
|
if (target.kind !== "main") return result("Session-scoped watchdog.configure currently supports target='main' only.", true);
|
|
@@ -159,3 +159,26 @@ export function formatWatchdogTurnDelta(input: WatchdogTurnDeltaInput): string {
|
|
|
159
159
|
if (input.finalAssistantStop) sections.push("Final assistant stop: stop without tool call");
|
|
160
160
|
return sections.join("\n\n---\n\n");
|
|
161
161
|
}
|
|
162
|
+
|
|
163
|
+
/** Only completed, exactly paired native orchestration calls are activity evidence. */
|
|
164
|
+
export function formatWatchdogOrchestrationActivity(event: unknown): string {
|
|
165
|
+
const turn = event as { type?: string; message?: { content?: unknown }; toolResults?: unknown[] } | undefined;
|
|
166
|
+
if (turn?.type !== "turn_end" || !Array.isArray(turn.message?.content) || !Array.isArray(turn.toolResults)) return "";
|
|
167
|
+
const sections: string[] = [];
|
|
168
|
+
for (const call of turn.message.content) {
|
|
169
|
+
if (call?.type !== "toolCall" || typeof call.id !== "string") continue;
|
|
170
|
+
const args = call.arguments;
|
|
171
|
+
if (!args || typeof args !== "object") continue;
|
|
172
|
+
const eligible = call.name === "bg_wait"
|
|
173
|
+
|| (call.name === "subagent_supervisor" && ["pending", "list", "reply"].includes(args.action))
|
|
174
|
+
|| (call.name === "subagent" && (["status", "resume", "interrupt", "steer", "stop"].includes(args.action)
|
|
175
|
+
|| (args.action === undefined && (typeof args.agent === "string" || typeof args.workflowScript === "string" || typeof args.workflowScriptPath === "string"))));
|
|
176
|
+
if (!eligible) continue;
|
|
177
|
+
const result = turn.toolResults.find((value) => {
|
|
178
|
+
const result = value as { role?: string; toolCallId?: string; toolName?: string };
|
|
179
|
+
return result?.role === "toolResult" && result.toolCallId === call.id && result.toolName === call.name;
|
|
180
|
+
});
|
|
181
|
+
if (result) sections.push(formatToolCall(call.name, args), formatWatchdogReviewMessage(result) ?? "");
|
|
182
|
+
}
|
|
183
|
+
return sections.join("\n\n");
|
|
184
|
+
}
|
package/src/watchdog/types.ts
CHANGED
|
@@ -15,8 +15,8 @@ export const WATCHDOG_WARNING_CATEGORIES = [
|
|
|
15
15
|
] as const;
|
|
16
16
|
export type WatchdogCategory = typeof WATCHDOG_WARNING_CATEGORIES[number];
|
|
17
17
|
|
|
18
|
-
export const
|
|
19
|
-
export type
|
|
18
|
+
export const WATCHDOG_WARNING_IMPORTANCES = ["low", "medium", "high"] as const;
|
|
19
|
+
export type WatchdogImportance = typeof WATCHDOG_WARNING_IMPORTANCES[number];
|
|
20
20
|
|
|
21
21
|
export const WATCHDOG_WARNING_SOURCES = ["main", "child", "lsp"] as const;
|
|
22
22
|
export type WatchdogWarningSource = typeof WATCHDOG_WARNING_SOURCES[number];
|
|
@@ -44,11 +44,11 @@ export type WatchdogWarningState = typeof WATCHDOG_WARNING_STATES[number];
|
|
|
44
44
|
|
|
45
45
|
export interface WatchdogWarning {
|
|
46
46
|
severity: WatchdogSeverity;
|
|
47
|
+
importance: WatchdogImportance;
|
|
47
48
|
summary: string;
|
|
48
49
|
evidence: string;
|
|
49
50
|
recommendedAction: string;
|
|
50
51
|
category?: WatchdogCategory;
|
|
51
|
-
confidence?: WatchdogConfidence;
|
|
52
52
|
source?: WatchdogWarningSource;
|
|
53
53
|
agent?: string;
|
|
54
54
|
runId?: string;
|
|
@@ -150,6 +150,8 @@ export interface WatchdogRulesConfig {
|
|
|
150
150
|
|
|
151
151
|
export interface ResolvedWatchdogConfig {
|
|
152
152
|
enabled: boolean;
|
|
153
|
+
/** Main-only, bounded boundary clarification; never enabled for child reviews. */
|
|
154
|
+
clarification: boolean;
|
|
153
155
|
agentEndTimeoutMs: number;
|
|
154
156
|
severityThreshold: WatchdogSeverity;
|
|
155
157
|
maxWarnings: number | null;
|
|
@@ -34,12 +34,12 @@ export function formatWatchdogWarningContent(warning: WatchdogWarning): string {
|
|
|
34
34
|
const details = normalizeWatchdogWarningDetails(warning);
|
|
35
35
|
const attrs = [
|
|
36
36
|
`severity="${escapeXmlAttribute(details.severity)}"`,
|
|
37
|
+
`importance="${escapeXmlAttribute(details.importance)}"`,
|
|
37
38
|
`category="${escapeXmlAttribute(details.category)}"`,
|
|
38
39
|
`source="${escapeXmlAttribute(details.source)}"`,
|
|
39
40
|
`guidance="weigh, don't blindly obey"`,
|
|
40
41
|
];
|
|
41
42
|
const optionalTags = [
|
|
42
|
-
tag("confidence", details.confidence),
|
|
43
43
|
tag("agent", details.agent),
|
|
44
44
|
tag("run_id", details.runId),
|
|
45
45
|
tag("state", details.state),
|