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
|
@@ -3,7 +3,7 @@ import { Text } from "@earendil-works/pi-tui";
|
|
|
3
3
|
import { resolveEffectiveThinking, splitKnownThinkingSuffix, THINKING_LEVELS, type ThinkingLevel } from "../shared/model-info.ts";
|
|
4
4
|
import { SLASH_TEXT_RESULT_TYPE } from "../shared/types.ts";
|
|
5
5
|
import { captureWatchdogDiffBaseline, type WatchdogDiffBaseline } from "./diff-tool.ts";
|
|
6
|
-
import {
|
|
6
|
+
import { formatWatchdogRecommendation, recommendWatchdogModel, resolveWatchdogModelInput, parseWatchdogThinkingInput } from "./model-selection.ts";
|
|
7
7
|
import { renderWatchdogWarning } from "./render.ts";
|
|
8
8
|
import { createMainWatchdogReview } from "./review.ts";
|
|
9
9
|
import { MainWatchdogRuntime, type WatchdogReviewFunction } from "./runtime.ts";
|
|
@@ -87,12 +87,11 @@ function childrenLine(snapshot: ReturnType<MainWatchdogRuntime["getSnapshot"]>):
|
|
|
87
87
|
return `Children: ${boolLabel(snapshot.config.enabled && children.enabled)} · model ${model} · thinking ${thinking}${overrideText}`;
|
|
88
88
|
}
|
|
89
89
|
|
|
90
|
-
function recommendationLine(ctx: ExtensionContext): string {
|
|
90
|
+
function recommendationLine(snapshot: ReturnType<MainWatchdogRuntime["getSnapshot"]>, ctx: ExtensionContext): string {
|
|
91
91
|
try {
|
|
92
|
-
|
|
93
|
-
return `Recommended strong watchdog: ${recommendation.model}:${recommendation.thinking} (${recommendation.label}, complementary reviewer)`;
|
|
92
|
+
return formatWatchdogRecommendation(recommendWatchdogModel(ctx, snapshot.config.main));
|
|
94
93
|
} catch (error) {
|
|
95
|
-
return `
|
|
94
|
+
return `Watchdog recommendation unavailable (${messageFromError(error)})`;
|
|
96
95
|
}
|
|
97
96
|
}
|
|
98
97
|
|
|
@@ -111,7 +110,8 @@ export function buildWatchdogStatus(snapshot: ReturnType<MainWatchdogRuntime["ge
|
|
|
111
110
|
"Subagent watchdog",
|
|
112
111
|
`Main: ${boolLabel(snapshot.enabled)}${!snapshot.config.enabled && snapshot.sessionOverride === undefined ? " (default off)" : ""}`,
|
|
113
112
|
`Runtime: ${statusLabel(snapshot.status)}${snapshot.bufferedDeltas > 0 ? ` · buffered deltas ${snapshot.bufferedDeltas}` : ""}`,
|
|
114
|
-
`Review trigger: ${snapshot.reviewTrigger === "repo-edits" ? "repo edits only" : "every non-empty turn delta"}`,
|
|
113
|
+
`Review trigger: ${snapshot.reviewTrigger === "repo-edits" ? snapshot.config.clarification ? "repo edits + bounded main orchestration activity" : "repo edits only" : "every non-empty turn delta"}`,
|
|
114
|
+
`Main clarification: ${snapshot.config.clarification ? "on" : "off"}`,
|
|
115
115
|
`Scope context: ${snapshot.config.scope.enabled ? "on" : "off"}`,
|
|
116
116
|
`Cadence: ${snapshot.config.cadence.everyNTools === null ? "boundary only" : `every ${snapshot.config.cadence.everyNTools} tools + boundary`}`,
|
|
117
117
|
lspLine(snapshot),
|
|
@@ -119,7 +119,7 @@ export function buildWatchdogStatus(snapshot: ReturnType<MainWatchdogRuntime["ge
|
|
|
119
119
|
mainModelLine(snapshot, ctx),
|
|
120
120
|
`Main thinking: ${mainThinkingLine(snapshot, ctx)}`,
|
|
121
121
|
childrenLine(snapshot),
|
|
122
|
-
recommendationLine(ctx),
|
|
122
|
+
recommendationLine(snapshot, ctx),
|
|
123
123
|
`Agent-end timeout: ${snapshot.config.agentEndTimeoutMs}ms`,
|
|
124
124
|
`Stalemate: ${snapshot.boundaryRepeats}/${snapshot.config.stalemateRepeats}${snapshot.stalemate ? " · stopped" : ""}`,
|
|
125
125
|
`Rules: ${snapshot.config.rules ? `${Object.keys(snapshot.config.rules.roleModels).length} role models · ${snapshot.config.rules.action}` : "none"}`,
|
|
@@ -145,7 +145,7 @@ export function buildWatchdogStatus(snapshot: ReturnType<MainWatchdogRuntime["ge
|
|
|
145
145
|
"",
|
|
146
146
|
"Model commands:",
|
|
147
147
|
"- /subagents-watchdog recommend-model",
|
|
148
|
-
"- /subagents-watchdog model recommended",
|
|
148
|
+
"- /subagents-watchdog model recommended (saves to user settings, not project settings)",
|
|
149
149
|
"- /subagents-watchdog model <provider/model[:thinking]>",
|
|
150
150
|
"- /subagents-watchdog model inherit",
|
|
151
151
|
"- /subagents-watchdog session model recommended",
|
|
@@ -154,10 +154,10 @@ export function buildWatchdogStatus(snapshot: ReturnType<MainWatchdogRuntime["ge
|
|
|
154
154
|
return lines.join("\n");
|
|
155
155
|
}
|
|
156
156
|
|
|
157
|
-
function parseTestCommand(input: string): { severity: "concern" | "blocker"; text: string } | undefined {
|
|
158
|
-
const match = input.match(/^test\s+(concern|blocker)\s+([\s\S]+)$/);
|
|
157
|
+
function parseTestCommand(input: string): { severity: "concern" | "blocker"; importance: "low" | "medium" | "high"; text: string } | undefined {
|
|
158
|
+
const match = input.match(/^test\s+(concern|blocker)\s+(low|medium|high)\s+([\s\S]+)$/);
|
|
159
159
|
if (!match) return undefined;
|
|
160
|
-
return { severity: match[1] as "concern" | "blocker",
|
|
160
|
+
return { severity: match[1] as "concern" | "blocker", importance: match[2] as "low" | "medium" | "high", text: match[3]!.trim() };
|
|
161
161
|
}
|
|
162
162
|
|
|
163
163
|
function formatThinking(value: ThinkingLevel | false | undefined): string {
|
|
@@ -171,12 +171,12 @@ function parseThinkingCommand(raw: string): ThinkingLevel | false | null {
|
|
|
171
171
|
return parseWatchdogThinkingInput(value, "/subagents-watchdog thinking") ?? null;
|
|
172
172
|
}
|
|
173
173
|
|
|
174
|
-
function resolveModelCommandValue(ctx: ExtensionCommandContext, raw: string): { model: string | null; thinking: ThinkingLevel | false | null; description: string } {
|
|
174
|
+
function resolveModelCommandValue(runtime: MainWatchdogRuntime, ctx: ExtensionCommandContext, raw: string): { model: string | null; thinking: ThinkingLevel | false | null; description: string } {
|
|
175
175
|
const value = raw.trim();
|
|
176
176
|
if (!value) throw new Error("Expected a model, 'recommended', or 'inherit'.");
|
|
177
177
|
if (value === "inherit") return { model: null, thinking: null, description: "current session model and thinking" };
|
|
178
178
|
if (value === "recommended") {
|
|
179
|
-
const recommendation =
|
|
179
|
+
const recommendation = recommendWatchdogModel(ctx, runtime.getSnapshot(ctx.cwd).config.main);
|
|
180
180
|
return {
|
|
181
181
|
model: recommendation.model,
|
|
182
182
|
thinking: recommendation.thinking,
|
|
@@ -191,18 +191,18 @@ function resolveModelCommandValue(ctx: ExtensionCommandContext, raw: string): {
|
|
|
191
191
|
};
|
|
192
192
|
}
|
|
193
193
|
|
|
194
|
-
function buildRecommendationText(ctx: ExtensionCommandContext): string {
|
|
195
|
-
const recommendation =
|
|
194
|
+
function buildRecommendationText(runtime: MainWatchdogRuntime, ctx: ExtensionCommandContext): string {
|
|
195
|
+
const recommendation = recommendWatchdogModel(ctx, runtime.getSnapshot(ctx.cwd).config.main);
|
|
196
196
|
return [
|
|
197
197
|
"Subagent watchdog recommended model",
|
|
198
198
|
`Current session: ${currentSessionModelLine(ctx as ExtensionContext)}`,
|
|
199
|
-
`Recommended: ${recommendation.model}:${recommendation.thinking}`,
|
|
199
|
+
recommendation.source === "configured" ? formatWatchdogRecommendation(recommendation) : `Recommended: ${recommendation.model}:${recommendation.thinking}`,
|
|
200
200
|
`Reason: ${recommendation.reason}`,
|
|
201
201
|
"",
|
|
202
202
|
"Apply for this session:",
|
|
203
203
|
"/subagents-watchdog session model recommended",
|
|
204
204
|
"",
|
|
205
|
-
"Save as your user default:",
|
|
205
|
+
"Save as your user default (affects other projects without overrides; does not change project settings):",
|
|
206
206
|
"/subagents-watchdog model recommended",
|
|
207
207
|
].join("\n");
|
|
208
208
|
}
|
|
@@ -221,20 +221,15 @@ function buildCheckText(runtime: MainWatchdogRuntime, ctx: ExtensionCommandConte
|
|
|
221
221
|
}
|
|
222
222
|
lines.push(`Main thinking: ${mainThinkingLine(snapshot, ctx as ExtensionContext)}`);
|
|
223
223
|
lines.push(lspLine(snapshot));
|
|
224
|
-
|
|
225
|
-
const recommendation = recommendStrongWatchdogModel(ctx as ExtensionContext);
|
|
226
|
-
lines.push(`Recommended strong watchdog: ${recommendation.model}:${recommendation.thinking}`);
|
|
227
|
-
} catch (error) {
|
|
228
|
-
lines.push(`Recommended strong watchdog: unavailable (${messageFromError(error)})`);
|
|
229
|
-
}
|
|
224
|
+
lines.push(recommendationLine(snapshot, ctx));
|
|
230
225
|
return lines.join("\n");
|
|
231
226
|
}
|
|
232
227
|
|
|
233
|
-
function createTestWarning(severity: "concern" | "blocker", text: string): WatchdogWarning {
|
|
228
|
+
function createTestWarning(severity: "concern" | "blocker", importance: "low" | "medium" | "high", text: string): WatchdogWarning {
|
|
234
229
|
return {
|
|
235
230
|
severity,
|
|
236
231
|
category: "other",
|
|
237
|
-
|
|
232
|
+
importance,
|
|
238
233
|
source: "main",
|
|
239
234
|
state: "displayed",
|
|
240
235
|
summary: text,
|
|
@@ -258,7 +253,7 @@ async function handleWatchdogCommand(
|
|
|
258
253
|
}
|
|
259
254
|
if (input === "recommend-model") {
|
|
260
255
|
try {
|
|
261
|
-
sendSlashText(pi, buildRecommendationText(ctx));
|
|
256
|
+
sendSlashText(pi, buildRecommendationText(runtime, ctx));
|
|
262
257
|
} catch (error) {
|
|
263
258
|
sendSlashText(pi, `Subagent watchdog recommended model\n\n${messageFromError(error)}`);
|
|
264
259
|
}
|
|
@@ -301,7 +296,7 @@ async function handleWatchdogCommand(
|
|
|
301
296
|
if (input.startsWith("session model ")) {
|
|
302
297
|
const rawModel = input.slice("session model ".length);
|
|
303
298
|
try {
|
|
304
|
-
const value = resolveModelCommandValue(ctx, rawModel);
|
|
299
|
+
const value = resolveModelCommandValue(runtime, ctx, rawModel);
|
|
305
300
|
const snapshot = value.model === null
|
|
306
301
|
? runtime.clearSessionModel(ctx.cwd)
|
|
307
302
|
: runtime.setSessionModel({ model: value.model, thinking: value.thinking ?? null }, ctx.cwd);
|
|
@@ -319,7 +314,7 @@ async function handleWatchdogCommand(
|
|
|
319
314
|
if (input.startsWith("model ")) {
|
|
320
315
|
const rawModel = input.slice("model ".length);
|
|
321
316
|
try {
|
|
322
|
-
const value = resolveModelCommandValue(ctx, rawModel);
|
|
317
|
+
const value = resolveModelCommandValue(runtime, ctx, rawModel);
|
|
323
318
|
const settingsPath = writeWatchdogModelSettings({
|
|
324
319
|
scope: "user",
|
|
325
320
|
target: { kind: "main" },
|
|
@@ -329,7 +324,7 @@ async function handleWatchdogCommand(
|
|
|
329
324
|
runtime.refreshConfig(ctx.cwd);
|
|
330
325
|
const snapshot = runtime.getSnapshot(ctx.cwd);
|
|
331
326
|
sendSlashText(pi, [
|
|
332
|
-
`Subagent watchdog model saved: ${value.description}.`,
|
|
327
|
+
`Subagent watchdog model saved to user settings: ${value.description}. Project and session overrides still take precedence.`,
|
|
333
328
|
`Updated: ${settingsPath}`,
|
|
334
329
|
`Main now: ${boolLabel(snapshot.enabled)}`,
|
|
335
330
|
value.model === null ? "The watchdog now inherits the current session model and thinking." : "Run /subagents-watchdog on if the watchdog is still off.",
|
|
@@ -365,15 +360,14 @@ async function handleWatchdogCommand(
|
|
|
365
360
|
const test = parseTestCommand(input);
|
|
366
361
|
if (test) {
|
|
367
362
|
if (!test.text) {
|
|
368
|
-
ctx.ui.notify("Usage: /subagents-watchdog test concern|blocker <text>", "error");
|
|
363
|
+
ctx.ui.notify("Usage: /subagents-watchdog test concern|blocker low|medium|high <text>", "error");
|
|
369
364
|
return;
|
|
370
365
|
}
|
|
371
|
-
const warning = createTestWarning(test.severity, test.text);
|
|
372
|
-
|
|
373
|
-
pi.sendMessage(createWatchdogWarningMessage(details, { display: true, details }));
|
|
366
|
+
const warning = createTestWarning(test.severity, test.importance, test.text);
|
|
367
|
+
runtime.displayRecordedWarning(warning);
|
|
374
368
|
return;
|
|
375
369
|
}
|
|
376
|
-
ctx.ui.notify(`Usage: /subagents-watchdog [status|on|off|session on|session off|recommend-model|model recommended|model <provider/model[:thinking]>|model inherit|thinking ${THINKING_LEVELS.join("|")}|thinking inherit|session model recommended|check|test concern
|
|
370
|
+
ctx.ui.notify(`Usage: /subagents-watchdog [status|on|off|session on|session off|recommend-model|model recommended|model <provider/model[:thinking]>|model inherit|thinking ${THINKING_LEVELS.join("|")}|thinking inherit|session model recommended|check|test concern|blocker low|medium|high <text>]`, "error");
|
|
377
371
|
}
|
|
378
372
|
|
|
379
373
|
export function registerMainWatchdog(pi: ExtensionAPI, options: RegisterMainWatchdogOptions = {}): MainWatchdogRuntime {
|
|
@@ -387,6 +381,8 @@ export function registerMainWatchdog(pi: ExtensionAPI, options: RegisterMainWatc
|
|
|
387
381
|
reviewDescription: options.review ? "injected seam" : "real model review",
|
|
388
382
|
reviewChangesOnly: true,
|
|
389
383
|
displayWarning: (details, options) => pi.sendMessage(createWatchdogWarningMessage(details, { display: true, details }), options),
|
|
384
|
+
displayUserWarning: (details) => pi.appendEntry(SUBAGENT_WATCHDOG_WARNING_TYPE, details),
|
|
385
|
+
displayClarification: (content) => pi.sendMessage({ customType: "subagent_watchdog_clarification", content, display: true }, { deliverAs: "steer", triggerTurn: true }),
|
|
390
386
|
});
|
|
391
387
|
|
|
392
388
|
pi.registerMessageRenderer<WatchdogWarningDetails>(SUBAGENT_WATCHDOG_WARNING_TYPE, (message, renderOptions, theme) => {
|
|
@@ -399,6 +395,12 @@ export function registerMainWatchdog(pi: ExtensionAPI, options: RegisterMainWatc
|
|
|
399
395
|
}
|
|
400
396
|
return renderWatchdogWarning(details, renderOptions, theme);
|
|
401
397
|
});
|
|
398
|
+
pi.registerEntryRenderer<WatchdogWarningDetails>(SUBAGENT_WATCHDOG_WARNING_TYPE, (entry, renderOptions, theme) => {
|
|
399
|
+
const details = entry.data as WatchdogWarningDetails | undefined;
|
|
400
|
+
return details?.summary && details.evidence && details.recommendedAction
|
|
401
|
+
? renderWatchdogWarning(details, renderOptions, theme)
|
|
402
|
+
: undefined;
|
|
403
|
+
});
|
|
402
404
|
|
|
403
405
|
pi.registerCommand("subagents-watchdog", {
|
|
404
406
|
description: "Show or toggle the default-off subagent watchdog",
|
|
@@ -421,6 +423,8 @@ export function registerMainWatchdog(pi: ExtensionAPI, options: RegisterMainWatc
|
|
|
421
423
|
rememberContext(ctx);
|
|
422
424
|
runtime.handleTurnEnd(event, ctx);
|
|
423
425
|
});
|
|
426
|
+
pi.on("input", (event) => { if (event.source !== "extension") runtime.handleUserInput(); });
|
|
427
|
+
pi.on("model_select", () => runtime.handleModelChange());
|
|
424
428
|
pi.on("tool_result", (_event, ctx) => {
|
|
425
429
|
rememberContext(ctx);
|
|
426
430
|
runtime.handleToolResult(ctx);
|
|
@@ -431,7 +435,7 @@ export function registerMainWatchdog(pi: ExtensionAPI, options: RegisterMainWatc
|
|
|
431
435
|
});
|
|
432
436
|
pi.on("session_before_switch", () => runtime.reset("session switch", { clearReviewInputSignature: true, clearLspLedger: true, clearScope: true }));
|
|
433
437
|
pi.on("session_before_fork", () => runtime.reset("session fork", { clearReviewInputSignature: true, clearLspLedger: true, clearScope: true }));
|
|
434
|
-
pi.on("session_compact", () => runtime.reset("session compact", {
|
|
438
|
+
pi.on("session_compact", () => runtime.reset("session compact", { clearActivity: true }));
|
|
435
439
|
pi.on("session_shutdown", () => {
|
|
436
440
|
currentContext = undefined;
|
|
437
441
|
runtime.dispose();
|
package/src/watchdog/render.ts
CHANGED
|
@@ -26,7 +26,7 @@ export function formatWatchdogWarningRenderText(warning: WatchdogWarningDetails)
|
|
|
26
26
|
`Subagent watchdog ${subject}${labels.length ? ` (${labels.join(", ")})` : ""}: ${warning.summary}`,
|
|
27
27
|
`Evidence: ${warning.evidence}`,
|
|
28
28
|
`Recommended action: ${warning.recommendedAction}`,
|
|
29
|
-
`Category: ${titleCase(warning.category)} · Source: ${warning.source}${warning.agent ? ` · Agent: ${warning.agent}` : ""}${warning.runId ? ` · Run: ${warning.runId}` : ""}`,
|
|
29
|
+
`Importance: ${titleCase(warning.importance)} · Category: ${titleCase(warning.category)} · Source: ${warning.source}${warning.agent ? ` · Agent: ${warning.agent}` : ""}${warning.runId ? ` · Run: ${warning.runId}` : ""}`,
|
|
30
30
|
];
|
|
31
31
|
if (warning.state === "failed" && warning.error) lines.push(`Failure: ${warning.error}`);
|
|
32
32
|
if (warning.state === "stalemate" && warning.stalemateRepeats !== undefined) {
|
package/src/watchdog/review.ts
CHANGED
|
@@ -3,32 +3,35 @@ import { createReadOnlyTools, convertToLlm, type ExtensionContext } from "@earen
|
|
|
3
3
|
import { streamSimple } from "@earendil-works/pi-ai/compat";
|
|
4
4
|
import type { Model, ProviderHeaders } from "@earendil-works/pi-ai";
|
|
5
5
|
import { Type, type Static } from "typebox";
|
|
6
|
-
import { resolveModelCandidate } from "../runs/shared/model-
|
|
6
|
+
import { resolveModelCandidate } from "../runs/shared/model-resolution.ts";
|
|
7
7
|
import { agentStreamOptions } from "../shared/agent-stream-options.ts";
|
|
8
|
+
import { opencodeSessionHeaders } from "../shared/opencode-session-headers.ts";
|
|
8
9
|
import { resolveEffectiveThinking, splitKnownThinkingSuffix, THINKING_LEVELS, toModelInfo } from "../shared/model-info.ts";
|
|
9
10
|
import { createWatchdogDiffTool, WATCHDOG_DIFF_TOOL_NAME, type WatchdogDiffBaseline } from "./diff-tool.ts";
|
|
10
11
|
import { loadWatchdogGuidance } from "./guidance.ts";
|
|
11
|
-
import type
|
|
12
|
+
import { boundWatchdogReviewText, type WatchdogReviewFunction, type WatchdogReviewRequest } from "./runtime.ts";
|
|
12
13
|
import {
|
|
13
14
|
WATCHDOG_WARNING_CATEGORIES,
|
|
14
|
-
|
|
15
|
+
WATCHDOG_WARNING_IMPORTANCES,
|
|
15
16
|
WATCHDOG_WARNING_SEVERITIES,
|
|
16
17
|
type ResolvedWatchdogConfig,
|
|
17
18
|
type WatchdogCategory,
|
|
18
|
-
type
|
|
19
|
+
type WatchdogImportance,
|
|
19
20
|
type WatchdogSeverity,
|
|
20
21
|
type WatchdogWarning,
|
|
21
22
|
} from "./types.ts";
|
|
22
23
|
|
|
23
24
|
const WATCHDOG_ALLOWED_TOOL_NAMES = new Set(["read", "grep", "find", "ls", "watchdog_warn", WATCHDOG_DIFF_TOOL_NAME]);
|
|
24
25
|
|
|
26
|
+
const WatchdogAskParams = Type.Object({ question: Type.String(), evidence: Type.String() }, { additionalProperties: false });
|
|
27
|
+
|
|
25
28
|
const WatchdogWarnParams = Type.Object({
|
|
26
29
|
severity: Type.String({ enum: WATCHDOG_WARNING_SEVERITIES, description: "concern for actionable risk, blocker for a likely wrong or unsafe outcome" }),
|
|
30
|
+
importance: Type.String({ enum: WATCHDOG_WARNING_IMPORTANCES, description: "low or medium for user-only visibility; high when the parent model must receive the finding" }),
|
|
27
31
|
summary: Type.String({ description: "One concise sentence naming the issue." }),
|
|
28
32
|
evidence: Type.String({ description: "Specific evidence from the turn delta or inspected files." }),
|
|
29
33
|
recommendedAction: Type.String({ description: "Specific action the parent should take before accepting or continuing." }),
|
|
30
34
|
category: Type.Optional(Type.String({ enum: WATCHDOG_WARNING_CATEGORIES })),
|
|
31
|
-
confidence: Type.Optional(Type.String({ enum: WATCHDOG_WARNING_CONFIDENCES })),
|
|
32
35
|
}, { additionalProperties: false });
|
|
33
36
|
|
|
34
37
|
type WatchdogWarnParams = Static<typeof WatchdogWarnParams>;
|
|
@@ -114,9 +117,16 @@ function resolveConfiguredModel(ctx: ExtensionContext, rawModel: string): { mode
|
|
|
114
117
|
return { model, modelString: resolved };
|
|
115
118
|
}
|
|
116
119
|
|
|
120
|
+
class WatchdogAuthError extends Error {}
|
|
121
|
+
|
|
117
122
|
async function resolveReviewAuth(ctx: ExtensionContext, model: RegistryModel): Promise<WatchdogReviewAuth> {
|
|
118
|
-
|
|
119
|
-
|
|
123
|
+
let auth;
|
|
124
|
+
try {
|
|
125
|
+
auth = await ctx.modelRegistry.getApiKeyAndHeaders(model);
|
|
126
|
+
} catch (error) {
|
|
127
|
+
throw new WatchdogAuthError(`Watchdog model auth failed for ${fullModelId(model)}: ${error instanceof Error ? error.message : String(error)}`, { cause: error });
|
|
128
|
+
}
|
|
129
|
+
if (auth.ok === false) throw new WatchdogAuthError(`Watchdog model auth failed for ${fullModelId(model)}: ${auth.error}`);
|
|
120
130
|
return {
|
|
121
131
|
...(auth.apiKey ? { apiKey: auth.apiKey } : {}),
|
|
122
132
|
...(auth.headers ? { headers: auth.headers } : {}),
|
|
@@ -172,8 +182,8 @@ function nonEmptyString(value: string, field: string): string {
|
|
|
172
182
|
function toWatchdogWarning(params: WatchdogWarnParams): WatchdogWarning {
|
|
173
183
|
return {
|
|
174
184
|
severity: params.severity as WatchdogSeverity,
|
|
185
|
+
importance: params.importance as WatchdogImportance,
|
|
175
186
|
category: (params.category ?? "other") as WatchdogCategory,
|
|
176
|
-
confidence: (params.confidence ?? "medium") as WatchdogConfidence,
|
|
177
187
|
source: "main",
|
|
178
188
|
summary: nonEmptyString(params.summary, "summary"),
|
|
179
189
|
evidence: nonEmptyString(params.evidence, "evidence"),
|
|
@@ -187,7 +197,7 @@ function createWatchdogWarnTool(request: WatchdogReviewRequest): AgentTool<typeo
|
|
|
187
197
|
label: "Watchdog warning",
|
|
188
198
|
description: [
|
|
189
199
|
"Emit one actionable main-session watchdog warning.",
|
|
190
|
-
"
|
|
200
|
+
"Set importance explicitly: low or medium for user-only visibility, high only when the parent model must receive the finding.",
|
|
191
201
|
"Do not use for nits, praise, informational notes, or clean reviews.",
|
|
192
202
|
].join(" "),
|
|
193
203
|
parameters: WatchdogWarnParams,
|
|
@@ -214,11 +224,11 @@ export function buildWatchdogSystemPrompt(ctx: Pick<ExtensionContext, "cwd">, op
|
|
|
214
224
|
"You are the main-session subagent watchdog for Pi.",
|
|
215
225
|
`Working directory: ${ctx.cwd}`,
|
|
216
226
|
"Review only the supplied parent turn delta. Inspect repository files only when needed to verify a concrete concern.",
|
|
217
|
-
options.hasScope ? "
|
|
227
|
+
options.hasScope ? "Use the Current scope record alongside supplied activity evidence; an unrelated user question does not cancel older authorized work." : undefined,
|
|
218
228
|
`You are read-only. You may use ${options.hasDiff ? "read, grep, find, ls, and watchdog_diff (the full repo diff since the session baseline; pass a path to narrow it)" : "read, grep, find, and ls"}. Do not edit files, run shell commands, spawn agents, or mutate state.`,
|
|
219
229
|
"Emit warnings only by calling watchdog_warn. Freeform assistant text is ignored and must not be used to report warnings.",
|
|
220
|
-
"Emit only
|
|
221
|
-
"Do not emit nits, style preferences,
|
|
230
|
+
"Emit only actionable concerns or blockers: missed user constraints, correctness risks, test gaps that matter, unsafe changes, stale facts, loop risks, or scope drift.",
|
|
231
|
+
"Do not emit nits, style preferences, unsupported guesses, informational notes, praise, or summaries.",
|
|
222
232
|
"If the turn is clean, call no tools and end normally.",
|
|
223
233
|
"Use severity='blocker' only when the issue should stop acceptance until addressed; otherwise use severity='concern'.",
|
|
224
234
|
guidance ? `\nStanding instructions from WATCHDOG.md (project first, then user):\n${guidance}` : undefined,
|
|
@@ -236,18 +246,6 @@ function buildReviewPrompt(request: WatchdogReviewRequest, selection: WatchdogRe
|
|
|
236
246
|
].join("\n\n");
|
|
237
247
|
}
|
|
238
248
|
|
|
239
|
-
function finalStopReason(agent: Agent): "stop" | "error" | "aborted" | "length" {
|
|
240
|
-
for (let index = agent.state.messages.length - 1; index >= 0; index--) {
|
|
241
|
-
const message = agent.state.messages[index];
|
|
242
|
-
if (message && typeof message === "object" && (message as { role?: unknown }).role === "assistant") {
|
|
243
|
-
const stopReason = (message as { stopReason?: unknown }).stopReason;
|
|
244
|
-
if (stopReason === "error" || stopReason === "aborted" || stopReason === "length") return stopReason;
|
|
245
|
-
return "stop";
|
|
246
|
-
}
|
|
247
|
-
}
|
|
248
|
-
return "stop";
|
|
249
|
-
}
|
|
250
|
-
|
|
251
249
|
function resolveContext(provider: WatchdogContextProvider): ExtensionContext | undefined {
|
|
252
250
|
return typeof provider === "function" ? provider() : provider;
|
|
253
251
|
}
|
|
@@ -256,59 +254,110 @@ export function createMainWatchdogReview(provider: WatchdogContextProvider, opti
|
|
|
256
254
|
return async (request) => {
|
|
257
255
|
const ctx = resolveContext(provider);
|
|
258
256
|
if (!ctx) throw new Error("Main watchdog review cannot run without an active Pi extension context.");
|
|
259
|
-
|
|
260
|
-
|
|
261
|
-
|
|
262
|
-
|
|
263
|
-
|
|
264
|
-
|
|
265
|
-
|
|
266
|
-
|
|
267
|
-
|
|
268
|
-
|
|
269
|
-
|
|
270
|
-
|
|
271
|
-
|
|
257
|
+
const aborted = () => ctx.signal?.aborted || request.signal?.aborted;
|
|
258
|
+
if (aborted()) return { stopReason: "aborted" };
|
|
259
|
+
try {
|
|
260
|
+
return (await runWatchdogAttempt(ctx, request, options)).result;
|
|
261
|
+
} catch (error) {
|
|
262
|
+
if (error instanceof WatchdogAuthError) throw error.cause ?? error;
|
|
263
|
+
throw error;
|
|
264
|
+
}
|
|
265
|
+
};
|
|
266
|
+
}
|
|
267
|
+
|
|
268
|
+
async function runWatchdogAttempt(ctx: ExtensionContext, request: WatchdogReviewRequest, options: CreateMainWatchdogReviewOptions): Promise<{
|
|
269
|
+
result: Awaited<ReturnType<WatchdogReviewFunction>>;
|
|
270
|
+
}> {
|
|
271
|
+
const selection = await resolveWatchdogReviewModel(ctx, request.config, {
|
|
272
|
+
currentThinkingLevel: options.getThinkingLevel?.(),
|
|
273
|
+
});
|
|
274
|
+
if (ctx.signal?.aborted || request.signal?.aborted) return { result: { stopReason: "aborted" } };
|
|
275
|
+
const auth = selection.auth;
|
|
276
|
+
const registeredProvider = (ctx.modelRegistry as {
|
|
277
|
+
getRegisteredProviderConfig?: (provider: string) => { api?: string; streamSimple?: StreamFn } | undefined;
|
|
278
|
+
}).getRegisteredProviderConfig?.(selection.model.provider);
|
|
279
|
+
const baseStreamFn = options.streamFn ?? (registeredProvider?.streamSimple && registeredProvider.api === selection.model.api
|
|
280
|
+
? registeredProvider.streamSimple
|
|
281
|
+
: streamSimple);
|
|
282
|
+
const sessionId = ctx.sessionManager.getSessionId();
|
|
283
|
+
const streamFn: StreamFn = (model, context, streamOptions) => {
|
|
284
|
+
// Agent may enter one final loop iteration after an aborted mixed tool batch.
|
|
285
|
+
// Never send that iteration to the provider after an intentional yield.
|
|
286
|
+
if (clarification) throw new Error("Watchdog review yielded for clarification.");
|
|
287
|
+
return baseStreamFn(model, context, {
|
|
272
288
|
...streamOptions,
|
|
273
289
|
...(auth.apiKey ? { apiKey: auth.apiKey } : {}),
|
|
274
290
|
env: auth.env || streamOptions?.env ? { ...(auth.env ?? {}), ...(streamOptions?.env ?? {}) } : undefined,
|
|
275
|
-
headers: { ...(streamOptions?.headers ?? {}), ...(auth.headers ?? {}) },
|
|
276
|
-
});
|
|
277
|
-
const diffBaseline = options.diffBaseline?.();
|
|
278
|
-
const tools = [
|
|
279
|
-
...(options.createReadOnlyTools ?? createReadOnlyTools)(ctx.cwd).filter((tool) => WATCHDOG_ALLOWED_TOOL_NAMES.has(tool.name) && tool.name !== "watchdog_warn"),
|
|
280
|
-
createWatchdogWarnTool(request),
|
|
281
|
-
...(diffBaseline ? [createWatchdogDiffTool(diffBaseline)] : []),
|
|
282
|
-
];
|
|
283
|
-
const agent = new Agent({
|
|
284
|
-
initialState: {
|
|
285
|
-
systemPrompt: buildWatchdogSystemPrompt(ctx, {
|
|
286
|
-
hasScope: request.hasScope,
|
|
287
|
-
guidance: loadWatchdogGuidance(ctx.cwd, request.config.guidance.watchdogMd),
|
|
288
|
-
hasDiff: diffBaseline !== undefined,
|
|
289
|
-
}),
|
|
290
|
-
model: selection.model,
|
|
291
|
-
thinkingLevel: selection.thinkingLevel,
|
|
292
|
-
tools,
|
|
293
|
-
},
|
|
294
|
-
convertToLlm,
|
|
295
|
-
...agentStreamOptions(streamFn),
|
|
296
|
-
getApiKey: (providerName) => providerName === selection.model.provider ? auth.apiKey : undefined,
|
|
297
|
-
beforeToolCall: async ({ toolCall }) => WATCHDOG_ALLOWED_TOOL_NAMES.has(toolCall.name)
|
|
298
|
-
? undefined
|
|
299
|
-
: { block: true, reason: `Watchdog reviews are read-only; tool '${toolCall.name}' is not allowed.` },
|
|
300
|
-
toolExecution: "sequential",
|
|
291
|
+
headers: { ...opencodeSessionHeaders(model, sessionId), ...(streamOptions?.headers ?? {}), ...(auth.headers ?? {}) },
|
|
301
292
|
});
|
|
302
|
-
|
|
303
|
-
|
|
304
|
-
|
|
305
|
-
|
|
306
|
-
|
|
307
|
-
|
|
308
|
-
|
|
309
|
-
|
|
310
|
-
|
|
311
|
-
|
|
312
|
-
|
|
293
|
+
};
|
|
294
|
+
const diffBaseline = options.diffBaseline?.();
|
|
295
|
+
let clarification: { question: string; evidence: string } | undefined;
|
|
296
|
+
let warned = false;
|
|
297
|
+
let toolCount = 0;
|
|
298
|
+
const warnRequest = request.allowClarification ? { ...request, emitWarning: (warning: WatchdogWarning) => {
|
|
299
|
+
if (clarification) return false;
|
|
300
|
+
const accepted = request.emitWarning(warning);
|
|
301
|
+
warned ||= accepted;
|
|
302
|
+
return accepted;
|
|
303
|
+
} } : request;
|
|
304
|
+
const tools = [
|
|
305
|
+
...(options.createReadOnlyTools ?? createReadOnlyTools)(ctx.cwd).filter((tool) => WATCHDOG_ALLOWED_TOOL_NAMES.has(tool.name) && tool.name !== "watchdog_warn"),
|
|
306
|
+
createWatchdogWarnTool(warnRequest),
|
|
307
|
+
...(diffBaseline ? [createWatchdogDiffTool(diffBaseline)] : []),
|
|
308
|
+
];
|
|
309
|
+
if (request.allowClarification) tools.push({
|
|
310
|
+
name: "watchdog_ask",
|
|
311
|
+
label: "Watchdog clarification",
|
|
312
|
+
description: "Send one focused question when missing task status, intent or other orchestrator context prevents a concrete review judgment. Yields and ends this review so the orchestrator can handle the message and continue; no answer or follow-up review is required. Never ask for approval or permission, or after recording a warning. Question and evidence are capped at 1000 and 2000 characters.",
|
|
313
|
+
parameters: WatchdogAskParams,
|
|
314
|
+
executionMode: "sequential",
|
|
315
|
+
async execute(_id, rawParams) {
|
|
316
|
+
const params = rawParams as Static<typeof WatchdogAskParams>;
|
|
317
|
+
if (warned || clarification || ctx.signal?.aborted || request.signal?.aborted) throw new Error("Clarification unavailable after warning, yield, or cancellation.");
|
|
318
|
+
if (!params.question.trim() || !params.evidence.trim()) throw new Error("A focused question and concrete evidence are required.");
|
|
319
|
+
clarification = { question: boundWatchdogReviewText(params.question.trim(), 1_000), evidence: boundWatchdogReviewText(params.evidence.trim(), 2_000) };
|
|
320
|
+
agent.abort(); // Intentional yield also stops mixed tool batches; terminate alone does not.
|
|
321
|
+
return { content: [{ type: "text", text: "Review yielded for clarification." }], details: {} };
|
|
322
|
+
},
|
|
323
|
+
});
|
|
324
|
+
const agent = new Agent({
|
|
325
|
+
initialState: {
|
|
326
|
+
systemPrompt: buildWatchdogSystemPrompt(ctx, {
|
|
327
|
+
hasScope: request.hasScope,
|
|
328
|
+
guidance: loadWatchdogGuidance(ctx.cwd, request.config.guidance.watchdogMd),
|
|
329
|
+
hasDiff: diffBaseline !== undefined,
|
|
330
|
+
}),
|
|
331
|
+
model: selection.model,
|
|
332
|
+
thinkingLevel: selection.thinkingLevel,
|
|
333
|
+
tools,
|
|
334
|
+
},
|
|
335
|
+
convertToLlm,
|
|
336
|
+
...agentStreamOptions(streamFn),
|
|
337
|
+
getApiKey: (providerName) => providerName === selection.model.provider ? auth.apiKey : undefined,
|
|
338
|
+
beforeToolCall: async ({ toolCall }) => !clarification && (WATCHDOG_ALLOWED_TOOL_NAMES.has(toolCall.name) || (request.allowClarification && toolCall.name === "watchdog_ask"))
|
|
339
|
+
? undefined
|
|
340
|
+
: { block: true, reason: `Watchdog reviews are read-only; tool '${toolCall.name}' is not allowed.` },
|
|
341
|
+
toolExecution: "sequential",
|
|
342
|
+
});
|
|
343
|
+
// Include rejected/invalid calls as well as read-only work and findings.
|
|
344
|
+
agent.subscribe((event) => { if (event.type === "tool_execution_start") toolCount++; });
|
|
345
|
+
const abort = () => agent.abort();
|
|
346
|
+
ctx.signal?.addEventListener("abort", abort, { once: true });
|
|
347
|
+
request.signal?.addEventListener("abort", abort, { once: true });
|
|
348
|
+
try {
|
|
349
|
+
if (ctx.signal?.aborted || request.signal?.aborted) return { result: { stopReason: "aborted" } };
|
|
350
|
+
await agent.prompt(buildReviewPrompt(request, selection));
|
|
351
|
+
} finally {
|
|
352
|
+
ctx.signal?.removeEventListener("abort", abort);
|
|
353
|
+
request.signal?.removeEventListener("abort", abort);
|
|
354
|
+
}
|
|
355
|
+
if (ctx.signal?.aborted || request.signal?.aborted) return { result: { stopReason: "aborted" } };
|
|
356
|
+
const terminal = agent.state.messages.findLast((message) => message.role === "assistant");
|
|
357
|
+
const reason = terminal && "stopReason" in terminal ? terminal.stopReason : undefined;
|
|
358
|
+
const stopReason = reason === "error" || reason === "aborted" || reason === "length" ? reason : "stop";
|
|
359
|
+
const error = terminal && "errorMessage" in terminal && typeof terminal.errorMessage === "string" ? terminal.errorMessage : undefined;
|
|
360
|
+
return {
|
|
361
|
+
result: clarification ? { clarification } : error ? { stopReason, errorMessage: error } : { stopReason },
|
|
313
362
|
};
|
|
314
363
|
}
|
package/src/watchdog/rules.ts
CHANGED
|
@@ -51,7 +51,7 @@ export function evaluateLaunchRule(rules: WatchdogRulesConfig | undefined, agent
|
|
|
51
51
|
}
|
|
52
52
|
|
|
53
53
|
export function ruleViolationWarning(violation: WatchdogRuleViolation): WatchdogWarning {
|
|
54
|
-
return { severity: "concern", category: "missed-constraint",
|
|
54
|
+
return { severity: "concern", category: "missed-constraint", importance: "high", source: "main", ...violation };
|
|
55
55
|
}
|
|
56
56
|
|
|
57
57
|
export function applyWatchdogLaunchRules(input: { cwd: string; agent: string; model?: string; warn?: (violation: WatchdogRuleViolation) => void }): string | undefined {
|