@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
|
@@ -14,7 +14,7 @@ import {
|
|
|
14
14
|
createCompletionBatcher,
|
|
15
15
|
resolveCompletionBatchConfig,
|
|
16
16
|
} from "./completion-batcher.ts";
|
|
17
|
-
import { SUBAGENT_ASYNC_COMPLETE_EVENT, SUBAGENT_FOREGROUND_COMPLETE_EVENT, type ParallelHandoffReference, type ScheduleOrigin, type SubagentState } from "../../shared/types.ts";
|
|
17
|
+
import { SUBAGENT_ASYNC_COMPLETE_EVENT, SUBAGENT_FOREGROUND_COMPLETE_EVENT, type ChildWatchdogProgress, type ChildWatchdogWarningSummary, type ParallelHandoffReference, type ScheduleOrigin, type SubagentState } from "../../shared/types.ts";
|
|
18
18
|
import { safeTerminalText } from "../../shared/display-text.ts";
|
|
19
19
|
import { resolveSubagentResultStatus } from "../../intercom/result-intercom.ts";
|
|
20
20
|
import { isUnexplainedProcessSignal } from "../shared/process-signal.ts";
|
|
@@ -31,6 +31,8 @@ export interface SubagentNotifyChildOutput {
|
|
|
31
31
|
previewUnavailableReason?: string;
|
|
32
32
|
}
|
|
33
33
|
|
|
34
|
+
export type SubagentNotifyWatchdogBlocker = Pick<ChildWatchdogWarningSummary, "summary" | "addressed" | "stalemate"> & { agent: string };
|
|
35
|
+
|
|
34
36
|
export interface SubagentNotifyDetails {
|
|
35
37
|
agent: string;
|
|
36
38
|
status: "completed" | "failed" | "paused" | "stopped";
|
|
@@ -47,6 +49,7 @@ export interface SubagentNotifyDetails {
|
|
|
47
49
|
handoffPath?: string;
|
|
48
50
|
/** Present when a durable schedule launched the run. */
|
|
49
51
|
scheduleOrigin?: ScheduleOrigin;
|
|
52
|
+
watchdogBlockers?: SubagentNotifyWatchdogBlocker[];
|
|
50
53
|
}
|
|
51
54
|
|
|
52
55
|
export interface CompletionNotification {
|
|
@@ -85,7 +88,9 @@ export interface CompletionNotification {
|
|
|
85
88
|
timedOut?: boolean;
|
|
86
89
|
stopped?: boolean;
|
|
87
90
|
turnBudgetExceeded?: boolean;
|
|
91
|
+
watchdog?: ChildWatchdogProgress;
|
|
88
92
|
}>;
|
|
93
|
+
watchdog?: ChildWatchdogProgress;
|
|
89
94
|
timestamp?: number;
|
|
90
95
|
durationMs?: number;
|
|
91
96
|
cwd?: string;
|
|
@@ -249,9 +254,28 @@ function formatCorrelationLines(details: SubagentNotifyDetails): string[] {
|
|
|
249
254
|
].filter((line): line is string => line !== undefined);
|
|
250
255
|
}
|
|
251
256
|
|
|
257
|
+
const WATCHDOG_BLOCKERS_HEADING = "Watchdog blockers:";
|
|
258
|
+
|
|
259
|
+
function formatWatchdogBlockerLines(details: SubagentNotifyDetails): string[] {
|
|
260
|
+
if (!details.watchdogBlockers?.length) return [];
|
|
261
|
+
return [WATCHDOG_BLOCKERS_HEADING, ...details.watchdogBlockers.map((blocker) => `- ${blocker.agent}: ${blocker.summary} (${blocker.stalemate ? "stalemate" : blocker.addressed ? "addressed" : "unaddressed"})`)];
|
|
262
|
+
}
|
|
263
|
+
|
|
264
|
+
// A stalemate blocker parses back as unaddressed; acceptance treats both as unresolved.
|
|
265
|
+
function parseWatchdogBlockerLines(lines: string[]): SubagentNotifyWatchdogBlocker[] {
|
|
266
|
+
const blockers: SubagentNotifyWatchdogBlocker[] = [];
|
|
267
|
+
for (const line of lines) {
|
|
268
|
+
const match = line.match(/^- (.+?): (.+) \((addressed|unaddressed|stalemate)\)$/);
|
|
269
|
+
if (!match) break;
|
|
270
|
+
blockers.push({ agent: match[1]!, summary: match[2]!, addressed: match[3] === "addressed", stalemate: match[3] === "stalemate" });
|
|
271
|
+
}
|
|
272
|
+
return blockers;
|
|
273
|
+
}
|
|
274
|
+
|
|
252
275
|
export function formatSingleCompletion(details: SubagentNotifyDetails): string {
|
|
253
276
|
const sessionLine = formatSessionLine(details);
|
|
254
277
|
const correlationLines = formatCorrelationLines(details);
|
|
278
|
+
const watchdogLines = formatWatchdogBlockerLines(details);
|
|
255
279
|
const taskKind = details.source === "foreground" ? "Detached foreground task" : "Background task";
|
|
256
280
|
const scheduleLine = details.scheduleOrigin
|
|
257
281
|
? `Scheduled run from **${details.scheduleOrigin.name ?? details.scheduleOrigin.id}** (schedule ${details.scheduleOrigin.id}).`
|
|
@@ -262,6 +286,7 @@ export function formatSingleCompletion(details: SubagentNotifyDetails): string {
|
|
|
262
286
|
scheduleLine,
|
|
263
287
|
scheduleLine ? "" : undefined,
|
|
264
288
|
formatResultPreview(details),
|
|
289
|
+
...(watchdogLines.length ? ["", ...watchdogLines] : []),
|
|
265
290
|
details.handoffPath ? "" : undefined,
|
|
266
291
|
details.handoffPath ? `Parallel handoff: ${details.handoffPath}` : undefined,
|
|
267
292
|
correlationLines.length && !details.handoffPath ? "" : undefined,
|
|
@@ -300,7 +325,9 @@ export function parseSubagentNotifyContent(content: string): SubagentNotifyDetai
|
|
|
300
325
|
const workflowRunIndex = body.findIndex((line) => line.startsWith("Workflow run: "));
|
|
301
326
|
const childRunsIndex = body.findIndex((line) => line.startsWith("Child runs: "));
|
|
302
327
|
const reconciledIndex = body.findIndex((line) => line.startsWith("Reconciled detached child: "));
|
|
303
|
-
const
|
|
328
|
+
const watchdogIndex = body.findIndex((line) => line === WATCHDOG_BLOCKERS_HEADING);
|
|
329
|
+
const watchdogBlockers = watchdogIndex >= 0 ? parseWatchdogBlockerLines(body.slice(watchdogIndex + 1)) : [];
|
|
330
|
+
const metadataIndexes = [sessionIndex, handoffIndex, workflowRunIndex, childRunsIndex, reconciledIndex, watchdogIndex].filter((index) => index >= 0);
|
|
304
331
|
const firstMetadataIndex = metadataIndexes.length ? Math.min(...metadataIndexes) : body.length;
|
|
305
332
|
const resultEnd = firstMetadataIndex > 0 && body[firstMetadataIndex - 1]?.trim() === "" ? firstMetadataIndex - 1 : firstMetadataIndex;
|
|
306
333
|
const resultPreview = body.slice(0, resultEnd).join("\n").trim() || "(no output)";
|
|
@@ -336,6 +363,7 @@ export function parseSubagentNotifyContent(content: string): SubagentNotifyDetai
|
|
|
336
363
|
...(workflowRunId ? { workflowRunId } : {}),
|
|
337
364
|
...(childRuns?.length ? { childRuns } : {}),
|
|
338
365
|
...(reconciledFromDetachedChild ? { reconciledFromDetachedChild } : {}),
|
|
366
|
+
...(watchdogBlockers.length ? { watchdogBlockers } : {}),
|
|
339
367
|
...(sessionLabel && sessionValue ? { sessionLabel, sessionValue } : {}),
|
|
340
368
|
};
|
|
341
369
|
}
|
|
@@ -349,6 +377,7 @@ export function formatGroupedCompletion(details: SubagentNotifyDetails[]): strin
|
|
|
349
377
|
const sessionLine = formatSessionLine(detail);
|
|
350
378
|
blocks.push(`${index + 1}. ${detail.agent}${detail.taskInfo ?? ""}${detail.scheduleOrigin ? ` — scheduled run from ${detail.scheduleOrigin.name ?? detail.scheduleOrigin.id} (schedule ${detail.scheduleOrigin.id})` : ""}`);
|
|
351
379
|
blocks.push(formatResultPreview(detail));
|
|
380
|
+
blocks.push(...formatWatchdogBlockerLines(detail));
|
|
352
381
|
if (detail.handoffPath) blocks.push(`Parallel handoff: ${detail.handoffPath}`);
|
|
353
382
|
blocks.push(...formatCorrelationLines(detail));
|
|
354
383
|
if (sessionLine) blocks.push(sessionLine);
|
|
@@ -420,6 +449,18 @@ export function buildCompletionDetails(result: CompletionNotification): Subagent
|
|
|
420
449
|
const handoffPath = typeof parallelHandoff?.path === "string" ? parallelHandoff.path : undefined;
|
|
421
450
|
const rawRunId = typeof result.runId === "string" ? result.runId : typeof result.id === "string" ? result.id : undefined;
|
|
422
451
|
const workflowRunId = (result.mode === "workflow" || agent === "workflow") && rawRunId ? rawRunId : undefined;
|
|
452
|
+
const directChild = !workflowRunId && result.results?.length === 1 ? result.results[0]! : undefined;
|
|
453
|
+
const directStructuredPreview = directChild
|
|
454
|
+
? childInlinePreview(directChild).preview
|
|
455
|
+
: undefined;
|
|
456
|
+
const directSummary = summary.trim();
|
|
457
|
+
const directAgent = typeof directChild?.agent === "string" ? directChild.agent : agent;
|
|
458
|
+
const directNoOutputSummary = directChild && (!directSummary
|
|
459
|
+
|| directSummary === "(no output)"
|
|
460
|
+
|| (directAgent && directSummary === `${directAgent}:\n(no output)`));
|
|
461
|
+
const resultPreview = directStructuredPreview && directNoOutputSummary
|
|
462
|
+
? `Structured output:\n${directStructuredPreview}`
|
|
463
|
+
: summary;
|
|
423
464
|
const childRuns = result.results?.flatMap((child) => {
|
|
424
465
|
const runId = typeof child.runId === "string" && child.runId.trim() ? child.runId.trim() : undefined;
|
|
425
466
|
const workflowKey = typeof child.workflowKey === "string" && child.workflowKey.trim() ? child.workflowKey.trim() : undefined;
|
|
@@ -450,6 +491,15 @@ export function buildCompletionDetails(result: CompletionNotification): Subagent
|
|
|
450
491
|
})
|
|
451
492
|
: undefined;
|
|
452
493
|
const reconciledFromDetachedChild = typeof result.reconciledFromDetachedChild === "string" ? result.reconciledFromDetachedChild : undefined;
|
|
494
|
+
const watchdogBlockers: SubagentNotifyWatchdogBlocker[] = [];
|
|
495
|
+
const collectWatchdogBlockers = (owner: string, progress: ChildWatchdogProgress | undefined) => {
|
|
496
|
+
for (const warning of progress?.warnings ?? []) {
|
|
497
|
+
if (warning.severity !== "blocker") continue;
|
|
498
|
+
watchdogBlockers.push({ agent: owner, summary: warning.summary, addressed: warning.addressed, stalemate: warning.stalemate });
|
|
499
|
+
}
|
|
500
|
+
};
|
|
501
|
+
collectWatchdogBlockers(agent, result.watchdog);
|
|
502
|
+
for (const child of result.results ?? []) collectWatchdogBlockers(typeof child.agent === "string" ? child.agent : agent, child.watchdog);
|
|
453
503
|
const session =
|
|
454
504
|
result.shareUrl
|
|
455
505
|
? { label: "Session", value: result.shareUrl }
|
|
@@ -468,13 +518,14 @@ export function buildCompletionDetails(result: CompletionNotification): Subagent
|
|
|
468
518
|
...(scheduleOrigin ? { scheduleOrigin } : {}),
|
|
469
519
|
...(result.source ? { source: result.source } : {}),
|
|
470
520
|
...(taskInfo ? { taskInfo } : {}),
|
|
471
|
-
resultPreview
|
|
521
|
+
resultPreview,
|
|
472
522
|
...(typeof result.durationMs === "number" ? { durationMs: result.durationMs } : {}),
|
|
473
523
|
...(handoffPath ? { handoffPath } : {}),
|
|
474
524
|
...(workflowRunId ? { workflowRunId } : {}),
|
|
475
525
|
...(childRuns.length ? { childRuns } : {}),
|
|
476
526
|
...(childOutputs?.length ? { childOutputs } : {}),
|
|
477
527
|
...(reconciledFromDetachedChild ? { reconciledFromDetachedChild } : {}),
|
|
528
|
+
...(watchdogBlockers.length ? { watchdogBlockers } : {}),
|
|
478
529
|
...(session ? { sessionLabel: session.label, sessionValue: session.value } : {}),
|
|
479
530
|
};
|
|
480
531
|
}
|
|
@@ -115,6 +115,22 @@ export function writeProcessTerminalCandidate(asyncDir: string, candidate: Proce
|
|
|
115
115
|
writePrivateAtomicJson(processTerminalCandidatePath(asyncDir), candidate);
|
|
116
116
|
}
|
|
117
117
|
|
|
118
|
+
/** Establish ownership before authorizing a runner to launch any child process. */
|
|
119
|
+
export function initializeProcessTerminal(asyncDir: string, runId: string, runnerProcessInstanceId: string): void {
|
|
120
|
+
writeProcessTerminalCandidate(asyncDir, {
|
|
121
|
+
version: 1,
|
|
122
|
+
runId,
|
|
123
|
+
runnerProcessInstanceId,
|
|
124
|
+
writers: {},
|
|
125
|
+
});
|
|
126
|
+
writeAtomicJson(processTerminalPath(asyncDir), {
|
|
127
|
+
version: 1,
|
|
128
|
+
state: "pending",
|
|
129
|
+
runId,
|
|
130
|
+
runnerProcessInstanceId,
|
|
131
|
+
});
|
|
132
|
+
}
|
|
133
|
+
|
|
118
134
|
export function markProcessTerminalCandidateLeaseRelease(asyncDir: string, token: string, acknowledged: boolean): void {
|
|
119
135
|
const candidate = readProcessTerminalCandidate(asyncDir);
|
|
120
136
|
if (!candidate || candidate.revivalLeaseToken !== token) return;
|
|
@@ -28,6 +28,8 @@ import { formatRunFanoutBudget, getRunFanoutBudgetSnapshot, readRunFanoutBudgetD
|
|
|
28
28
|
import { workflowGraphStageNodes } from "../shared/workflow-graph.ts";
|
|
29
29
|
import { getExternalJobProvider } from "../../api/external-job-provider.ts";
|
|
30
30
|
import { formatTimeoutRecoveryLines } from "../shared/mutation-evidence.ts";
|
|
31
|
+
import { formatWorkflowChecklistText, projectWorkflowChecklist } from "../../workflows/workflow-checklist.ts";
|
|
32
|
+
import { validHostStepNodes } from "../shared/host-step-status.ts";
|
|
31
33
|
|
|
32
34
|
interface RunStatusParams {
|
|
33
35
|
action?: string;
|
|
@@ -74,7 +76,7 @@ function formatWorkflowDebug(status: AsyncStatus): string[] {
|
|
|
74
76
|
status.lane ? `Lane: ${status.lane.key}${status.lane.mode ? ` (${status.lane.mode})` : ""}` : undefined,
|
|
75
77
|
].filter((line): line is string => line !== undefined);
|
|
76
78
|
for (const [index, step] of (status.steps ?? []).entries()) {
|
|
77
|
-
lines.push(` ${index + 1}. key ${step.workflowKey ?? "n/a"} · ${runStatusStepDisplayName(step)} · ${step.status} · async ${step.async === undefined ? "unknown" : step.async ? "yes" : "no"}${step.runId ? ` · run ${step.runId}` : ""}${step.lane ? ` · lane ${step.lane.key}` : ""}${step.worktreePath ? ` · worktree ${step.worktreePath} · branch ${step.branch ?? "unknown"}` : ""}`);
|
|
79
|
+
lines.push(` ${index + 1}. key ${step.workflowKey ?? "n/a"} · ${runStatusStepDisplayName(step)} · ${step.status} · async ${step.async === undefined ? "unknown" : step.async ? "yes" : "no"}${step.runId ? ` · run ${step.runId}` : ""}${step.lane ? ` · lane ${step.lane.key}` : ""}${step.worktreePath ? ` · worktree ${step.worktreePath} · branch ${step.branch ?? "unknown"}${step.provider ? ` · provider ${step.provider}` : ""}` : ""}`);
|
|
78
80
|
}
|
|
79
81
|
return lines;
|
|
80
82
|
}
|
|
@@ -522,6 +524,14 @@ export function inspectSubagentStatus(params: RunStatusParams, deps: RunStatusDe
|
|
|
522
524
|
status.mode === "workflow" && workflowReturnPreview !== undefined ? `Return: ${workflowReturnPreview}` : undefined,
|
|
523
525
|
status.mode === "workflow" && workflowEmitPreview !== undefined ? `Latest emit: ${workflowEmitPreview}` : undefined,
|
|
524
526
|
`Progress: ${progressLabel}`,
|
|
527
|
+
...(status.mode === "workflow" ? formatWorkflowChecklistText(projectWorkflowChecklist({
|
|
528
|
+
graph: status.workflowGraph,
|
|
529
|
+
steps: status.steps,
|
|
530
|
+
hostSteps: validHostStepNodes(status.workflowGraph),
|
|
531
|
+
preflight: status.preflight,
|
|
532
|
+
trace: status.workflow?.trace,
|
|
533
|
+
now: status.lastUpdate ?? status.endedAt ?? Date.now(),
|
|
534
|
+
}), "", { includeItems: false }) : []),
|
|
525
535
|
status.pendingAppends ? `Pending appends: ${status.pendingAppends}` : undefined,
|
|
526
536
|
`Started: ${started}`,
|
|
527
537
|
`Updated: ${updated}`,
|
|
@@ -551,6 +561,9 @@ export function inspectSubagentStatus(params: RunStatusParams, deps: RunStatusDe
|
|
|
551
561
|
const display = runStatusStepDisplayName(step);
|
|
552
562
|
const phase = step.phase ? `[${step.phase}] ` : "";
|
|
553
563
|
lines.push(`${stepLineLabel(status, index)}: ${phase}${display} ${step.status}${modelText}${stepActivityText ? `, ${stepActivityText}` : ""}${steeringSuffix}${acceptanceText}${budgetText}${errorText}`);
|
|
564
|
+
const structuredOutputPreview = step.structuredOutput === undefined ? undefined : formatWorkflowJsonPreview(step.structuredOutput, 4_000);
|
|
565
|
+
if (structuredOutputPreview !== undefined) lines.push(` Structured output: ${structuredOutputPreview}`);
|
|
566
|
+
if (step.structuredOutputPath) lines.push(` Structured output path: ${step.structuredOutputPath}`);
|
|
554
567
|
lines.push(...formatTimeoutRecoveryLines(step.timeoutRecovery, " "));
|
|
555
568
|
if (step.runner?.type === "external-cli") {
|
|
556
569
|
const runner = normalizeExternalCliRunnerStatus(step.runner);
|
|
@@ -670,7 +683,14 @@ export function inspectSubagentStatus(params: RunStatusParams, deps: RunStatusDe
|
|
|
670
683
|
if (data.parallelHandoff?.path) lines.push(`Parallel handoff: ${data.parallelHandoff.path}`);
|
|
671
684
|
const children = Array.isArray(data.results) ? data.results : data.agent ? [{ agent: data.agent, sessionFile: data.sessionFile }] : [];
|
|
672
685
|
lines.push(...formatTimeoutRecoveryLines(data.timeoutRecovery, " "));
|
|
673
|
-
for (const child of children
|
|
686
|
+
for (const [index, child] of children.entries()) {
|
|
687
|
+
const structuredOutput = (child as { structuredOutput?: unknown }).structuredOutput;
|
|
688
|
+
const structuredOutputPreview = structuredOutput === undefined ? undefined : formatWorkflowJsonPreview(structuredOutput, 4_000);
|
|
689
|
+
if (structuredOutputPreview !== undefined) lines.push(` Structured output${children.length > 1 ? ` (${index + 1})` : ""}: ${structuredOutputPreview}`);
|
|
690
|
+
const structuredOutputPath = (child as { structuredOutputPath?: unknown }).structuredOutputPath;
|
|
691
|
+
if (typeof structuredOutputPath === "string" && structuredOutputPath.trim()) lines.push(` Structured output path${children.length > 1 ? ` (${index + 1})` : ""}: ${structuredOutputPath}`);
|
|
692
|
+
lines.push(...formatTimeoutRecoveryLines(child.timeoutRecovery, " "));
|
|
693
|
+
}
|
|
674
694
|
lines.push(formatResumeGuidance(runId, children, data.sessionFile, { stopped: status === "stopped" }));
|
|
675
695
|
if (data.summary) lines.push("", data.summary);
|
|
676
696
|
const workflowChildren = parseWorkflowChildSummary((data as unknown as Record<string, unknown>).workflowChildren);
|
|
@@ -52,6 +52,8 @@ export interface ScheduleRecord {
|
|
|
52
52
|
catchUp: "none" | "latest";
|
|
53
53
|
timeoutMs?: number;
|
|
54
54
|
paused: boolean;
|
|
55
|
+
sessionOnly?: boolean;
|
|
56
|
+
ownerSessionFile?: string;
|
|
55
57
|
createdAt: string;
|
|
56
58
|
updatedAt: string;
|
|
57
59
|
activeRunId?: string;
|
|
@@ -72,6 +74,8 @@ export interface ScheduleRunRecord {
|
|
|
72
74
|
error?: string;
|
|
73
75
|
}
|
|
74
76
|
|
|
77
|
+
type PublicScheduleRecord = Omit<ScheduleRecord, "ownerSessionFile">;
|
|
78
|
+
|
|
75
79
|
type ScheduledRunManagerDeps = {
|
|
76
80
|
config: ExtensionConfig;
|
|
77
81
|
launch(params: SubagentParamsLike, ctx: ExtensionContext, signal: AbortSignal): Promise<AgentToolResult<Details>>;
|
|
@@ -295,6 +299,8 @@ function parseSchedule(value: unknown, file: string): ScheduleRecord {
|
|
|
295
299
|
} else if (record.trigger.kind === "interval") {
|
|
296
300
|
if (typeof record.trigger.every !== "string" || typeof record.trigger.everyMs !== "number" || typeof record.trigger.anchorAt !== "string" || typeof record.trigger.nextRunAt !== "string") throw new Error(`Schedule record '${file}' has an invalid interval trigger.`);
|
|
297
301
|
} else throw new Error(`Schedule record '${file}' has an unsupported trigger.`);
|
|
302
|
+
if (record.sessionOnly !== undefined && typeof record.sessionOnly !== "boolean") throw new Error(`Schedule record '${file}' has invalid sessionOnly.`);
|
|
303
|
+
if (record.sessionOnly === true && (typeof record.ownerSessionFile !== "string" || !record.ownerSessionFile.trim())) throw new Error(`Schedule record '${file}' is session-only but has no owner session file.`);
|
|
298
304
|
return { ...record, target: parseScheduleTarget(record.target, file) } as ScheduleRecord;
|
|
299
305
|
}
|
|
300
306
|
|
|
@@ -399,13 +405,19 @@ function duePlannedAt(schedule: ScheduleRecord, now: number): number | undefined
|
|
|
399
405
|
}
|
|
400
406
|
|
|
401
407
|
function textResult(text: string, schedules?: ScheduleRecord[], runs?: ScheduleRunRecord[], isError = false): AgentToolResult<Details> {
|
|
408
|
+
const publicSchedules = schedules?.map(publicScheduleRecord);
|
|
402
409
|
return {
|
|
403
410
|
content: [{ type: "text", text }],
|
|
404
411
|
...(isError ? { isError: true } : {}),
|
|
405
|
-
details: { mode: "management", results: [], schedules: { ...(
|
|
412
|
+
details: { mode: "management", results: [], schedules: { ...(publicSchedules ? { records: publicSchedules } : {}), ...(runs ? { runs } : {}) } },
|
|
406
413
|
};
|
|
407
414
|
}
|
|
408
415
|
|
|
416
|
+
function publicScheduleRecord(schedule: ScheduleRecord): PublicScheduleRecord {
|
|
417
|
+
const { ownerSessionFile: _ownerSessionFile, ...rest } = schedule;
|
|
418
|
+
return rest;
|
|
419
|
+
}
|
|
420
|
+
|
|
409
421
|
function targetLabel(target: ScheduleTarget): string {
|
|
410
422
|
const preview = previewSimpleWorkflowRun(target.workflowScript);
|
|
411
423
|
return preview?.agent ? `workflowScript -> agent ${preview.agent}` : "workflowScript (dynamic)";
|
|
@@ -450,6 +462,32 @@ function snapshotContext(ctx: ExtensionContext, cwd: string): ExtensionContext {
|
|
|
450
462
|
return { ...ctx, cwd, sessionManager };
|
|
451
463
|
}
|
|
452
464
|
|
|
465
|
+
/**
|
|
466
|
+
* 规范化会话文件路径, 兼容 Windows 路径大小写差异.
|
|
467
|
+
*
|
|
468
|
+
* @param value 会话文件路径
|
|
469
|
+
* @returns 规范化后的路径, 空值时返回 undefined
|
|
470
|
+
*/
|
|
471
|
+
function normalizedSessionFile(value: string | undefined): string | undefined {
|
|
472
|
+
if (!value || !value.trim()) return undefined;
|
|
473
|
+
const normalized = path.normalize(path.resolve(value));
|
|
474
|
+
return process.platform === "win32" ? normalized.toLowerCase() : normalized;
|
|
475
|
+
}
|
|
476
|
+
|
|
477
|
+
/**
|
|
478
|
+
* 判断 Schedule 是否属于当前 Pi 会话.
|
|
479
|
+
*
|
|
480
|
+
* @param schedule Schedule 记录
|
|
481
|
+
* @param ctx 当前 Pi 会话上下文
|
|
482
|
+
* @returns 是否允许当前会话执行该 Schedule
|
|
483
|
+
*/
|
|
484
|
+
function scheduleBelongsToSession(schedule: ScheduleRecord, ctx: ExtensionContext): boolean {
|
|
485
|
+
if (schedule.sessionOnly !== true) return true;
|
|
486
|
+
const ownerSessionFile = normalizedSessionFile(schedule.ownerSessionFile);
|
|
487
|
+
const currentSessionFile = normalizedSessionFile(ctx.sessionManager.getSessionFile());
|
|
488
|
+
return ownerSessionFile !== undefined && ownerSessionFile === currentSessionFile;
|
|
489
|
+
}
|
|
490
|
+
|
|
453
491
|
export function listScheduledRunSummaries(cwd: string, root?: string): ScheduleRecord[] {
|
|
454
492
|
return new ScheduleStore(scheduledRunStorePath(cwd, undefined, root), root === undefined ? path.resolve(cwd) : undefined).list();
|
|
455
493
|
}
|
|
@@ -560,6 +598,10 @@ export class ScheduledRunManager {
|
|
|
560
598
|
if (params.catchUp !== undefined && params.catchUp !== "none" && params.catchUp !== "latest") return textResult("catchUp must be 'none' or 'latest'.", undefined, undefined, true);
|
|
561
599
|
if (params.missionId !== undefined || params.mission !== undefined || params.missionUpdate !== undefined || params.missionStatus !== undefined || params.missionScope !== undefined) return textResult("Mission attachment is deferred from this first schedule slice.", undefined, undefined, true);
|
|
562
600
|
if (params.on !== undefined || params.timezone !== undefined || every === "day" || every === "week" || every === "month" || every === "year") return textResult("Calendar schedules are deferred from this first safe slice. Use a fixed interval such as every:'24h' or every:'7d'.", undefined, undefined, true);
|
|
601
|
+
const sessionOnly = params.sessionOnly === true;
|
|
602
|
+
if (sessionOnly && params.cwd !== undefined && !samePath(params.cwd, ctx.cwd)) return textResult("sessionOnly schedules cannot use an explicit cross-project cwd.", undefined, undefined, true);
|
|
603
|
+
const ownerSessionFile = sessionOnly ? ctx.sessionManager.getSessionFile() : undefined;
|
|
604
|
+
if (sessionOnly && !ownerSessionFile) return textResult("sessionOnly schedules require a persisted current session.", undefined, undefined, true);
|
|
563
605
|
const sessionId = ctx.sessionManager.getSessionId() ?? "unknown";
|
|
564
606
|
if (this.deps.resolveCapabilityCeiling?.(sessionId)) return textResult("Cannot persist a schedule while a capability ceiling is active.", undefined, undefined, true);
|
|
565
607
|
const pendingCount = store.list().filter(hasPendingScheduleWork).length;
|
|
@@ -587,24 +629,25 @@ export class ScheduledRunManager {
|
|
|
587
629
|
catchUp: params.catchUp ?? "latest",
|
|
588
630
|
...(params.timeoutMs === undefined ? {} : { timeoutMs: params.timeoutMs }),
|
|
589
631
|
paused: false,
|
|
632
|
+
...(sessionOnly ? { sessionOnly: true, ownerSessionFile: path.resolve(ownerSessionFile!) } : {}),
|
|
590
633
|
createdAt: timestamp(now),
|
|
591
634
|
updatedAt: timestamp(now),
|
|
592
635
|
};
|
|
593
636
|
store.write(schedule);
|
|
594
637
|
store.appendEvent(schedule, "schedule.created");
|
|
595
638
|
this.arm(schedule, store);
|
|
596
|
-
return textResult(`Created schedule ${id}.\nName: ${schedule.name}\nTrigger: ${at ? `at ${at}` : `every ${every}`}\nNext: ${schedule.trigger.nextRunAt}\nTarget: ${targetLabel(schedule.target)}`, [schedule]);
|
|
639
|
+
return textResult(`Created schedule ${id}.\nName: ${schedule.name}\nTrigger: ${at ? `at ${at}` : `every ${every}`}\nSession only: ${schedule.sessionOnly === true ? "yes" : "no"}\nNext: ${schedule.trigger.nextRunAt}\nTarget: ${targetLabel(schedule.target)}`, [schedule]);
|
|
597
640
|
}
|
|
598
641
|
|
|
599
642
|
private list(): AgentToolResult<Details> {
|
|
600
643
|
const schedules = this.requireStore().list().sort((a, b) => (a.trigger.nextRunAt ?? "").localeCompare(b.trigger.nextRunAt ?? ""));
|
|
601
644
|
if (!schedules.length) return textResult("No project schedules.", []);
|
|
602
|
-
return textResult([`Project schedules: ${schedules.length}`, ...schedules.map((item) => `- ${item.id} | ${item.paused ? "paused" : item.activeRunId ? "running" : "scheduled"} | ${item.trigger.nextRunAt ?? "no next run"} | ${item.name}`)].join("\n"), schedules);
|
|
645
|
+
return textResult([`Project schedules: ${schedules.length}`, ...schedules.map((item) => `- ${item.id} | ${item.paused ? "paused" : item.activeRunId ? "running" : "scheduled"} | ${item.trigger.nextRunAt ?? "no next run"} | ${item.sessionOnly === true ? "session-only" : "project"} | ${item.name}`)].join("\n"), schedules);
|
|
603
646
|
}
|
|
604
647
|
|
|
605
648
|
private show(params: SubagentParamsLike): AgentToolResult<Details> {
|
|
606
649
|
const schedule = this.resolve(params);
|
|
607
|
-
return textResult([`Schedule: ${schedule.id}`, `Name: ${schedule.name}`, `State: ${schedule.paused ? "paused" : schedule.activeRunId ? "running" : "scheduled"}`, `Target: ${targetLabel(schedule.target)}`, `CWD: ${shortenPath(schedule.cwd)}`, `Next: ${schedule.trigger.nextRunAt ?? "none"}`, `Catch up: ${schedule.catchUp}`, schedule.activeRunId ? `Active run: ${schedule.activeRunId}` : undefined].filter(Boolean).join("\n"), [schedule]);
|
|
650
|
+
return textResult([`Schedule: ${schedule.id}`, `Name: ${schedule.name}`, `State: ${schedule.paused ? "paused" : schedule.activeRunId ? "running" : "scheduled"}`, `Session only: ${schedule.sessionOnly === true ? "yes" : "no"}`, `Target: ${targetLabel(schedule.target)}`, `CWD: ${shortenPath(schedule.cwd)}`, `Next: ${schedule.trigger.nextRunAt ?? "none"}`, `Catch up: ${schedule.catchUp}`, schedule.activeRunId ? `Active run: ${schedule.activeRunId}` : undefined].filter(Boolean).join("\n"), [schedule]);
|
|
608
651
|
}
|
|
609
652
|
|
|
610
653
|
private history(params: SubagentParamsLike): AgentToolResult<Details> {
|
|
@@ -628,13 +671,18 @@ export class ScheduledRunManager {
|
|
|
628
671
|
private async runManual(params: SubagentParamsLike): Promise<AgentToolResult<Details>> {
|
|
629
672
|
const store = this.requireStore();
|
|
630
673
|
const schedule = this.resolve(params);
|
|
674
|
+
const context = this.requireContext(store);
|
|
675
|
+
if (!scheduleBelongsToSession(schedule, context)) {
|
|
676
|
+
return textResult(`Skipped schedule ${schedule.id}: current session is not its owner.`, [schedule]);
|
|
677
|
+
}
|
|
631
678
|
const run = await this.launch(store, schedule, this.now(), "manual", false);
|
|
632
679
|
return textResult(`Manual schedule run ${run.id}: ${run.state}${run.asyncId ? ` (async ${run.asyncId})` : ""}.`, [store.get(schedule.id)], [run], run.state === "failed_launch");
|
|
633
680
|
}
|
|
634
681
|
|
|
635
682
|
private async runDue(): Promise<AgentToolResult<Details>> {
|
|
636
683
|
const store = this.requireStore();
|
|
637
|
-
const
|
|
684
|
+
const context = this.requireContext(store);
|
|
685
|
+
const due = store.list().filter((schedule) => scheduleBelongsToSession(schedule, context) && !schedule.paused && nextRunAt(schedule) !== undefined && nextRunAt(schedule)! <= this.now());
|
|
638
686
|
const runs: ScheduleRunRecord[] = [];
|
|
639
687
|
for (const schedule of due) {
|
|
640
688
|
const planned = duePlannedAt(schedule, this.now())!;
|
|
@@ -659,6 +707,7 @@ export class ScheduledRunManager {
|
|
|
659
707
|
}
|
|
660
708
|
|
|
661
709
|
private restoreOne(store: ScheduleStore, schedule: ScheduleRecord, notBefore?: number, rearm = true): void {
|
|
710
|
+
if (!scheduleBelongsToSession(schedule, this.requireContext(store))) return;
|
|
662
711
|
if (schedule.activeRunId) {
|
|
663
712
|
const run = store.history(schedule.id).find((item) => item.id === schedule.activeRunId);
|
|
664
713
|
if (run?.state === "running" && run.asyncId) this.observedAsyncIds.add(run.asyncId);
|
|
@@ -737,6 +786,7 @@ export class ScheduledRunManager {
|
|
|
737
786
|
// is then nothing to run and nothing to re-arm.
|
|
738
787
|
const schedule = store.find(id);
|
|
739
788
|
if (!schedule) return;
|
|
789
|
+
if (!scheduleBelongsToSession(schedule, this.requireContext(store))) return;
|
|
740
790
|
const planned = duePlannedAt(schedule, this.now());
|
|
741
791
|
if (planned === undefined || schedule.paused) return;
|
|
742
792
|
if (planned > this.now()) return this.arm(schedule, store);
|
|
@@ -857,13 +907,20 @@ export class ScheduledRunManager {
|
|
|
857
907
|
private selectProject(cwd: string, ctx: ExtensionContext): void {
|
|
858
908
|
const projectCwd = path.resolve(cwd);
|
|
859
909
|
const root = scheduledRunStorePath(projectCwd, undefined, this.deps.storeRoot);
|
|
860
|
-
|
|
910
|
+
const isBoundContext = path.resolve(ctx.cwd) === projectCwd;
|
|
911
|
+
const previousContext = this.contexts.get(root);
|
|
912
|
+
const contextChanged = isBoundContext
|
|
913
|
+
&& previousContext !== undefined
|
|
914
|
+
&& normalizedSessionFile(previousContext.sessionManager.getSessionFile()) !== normalizedSessionFile(ctx.sessionManager.getSessionFile());
|
|
915
|
+
if (isBoundContext) this.contexts.set(root, snapshotContext(ctx, projectCwd));
|
|
861
916
|
else if (!this.contexts.has(root)) throw new Error(`Cannot use project '${projectCwd}' until that project has been opened in this runtime.`);
|
|
862
917
|
let store = this.stores.get(root);
|
|
863
918
|
if (!store) {
|
|
864
919
|
store = new ScheduleStore(root, this.deps.storeRoot === undefined ? projectCwd : undefined);
|
|
865
920
|
this.stores.set(root, store);
|
|
866
921
|
this.restore(store);
|
|
922
|
+
} else if (contextChanged) {
|
|
923
|
+
this.restore(store);
|
|
867
924
|
}
|
|
868
925
|
this.store = store;
|
|
869
926
|
}
|
|
@@ -23,7 +23,10 @@ export function steeringMessagePreview(message: string): string {
|
|
|
23
23
|
}
|
|
24
24
|
|
|
25
25
|
export function steeringReceipt(message: string, receipt: string): string {
|
|
26
|
-
|
|
26
|
+
const preview = steeringMessagePreview(message);
|
|
27
|
+
const longestFence = Math.max(2, ...[...preview.matchAll(/`{3,}/g)].map((match) => match[0]!.length));
|
|
28
|
+
const fence = "`".repeat(longestFence + 1);
|
|
29
|
+
return `${receipt}\n\nMessage sent:\n${fence}text\n${preview}\n${fence}`;
|
|
27
30
|
}
|
|
28
31
|
|
|
29
32
|
export function createSteeringStatus(): SteeringStatus {
|