zob-harness 0.11.0 → 0.12.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/.pi/extensions/zob-harness/index.ts +2 -0
- package/.pi/extensions/zob-harness/src/runtime/commands/misc.ts +50 -1
- package/.pi/extensions/zob-harness/src/runtime/delegation-feed.ts +6 -2
- package/.pi/extensions/zob-harness/src/runtime/delegation-monitor.ts +29 -1
- package/.pi/extensions/zob-harness/src/runtime/delegation-overlay.ts +5 -3
- package/.pi/extensions/zob-harness/src/runtime/events.ts +41 -0
- package/.pi/extensions/zob-harness/src/runtime/schemas.ts +1 -0
- package/.pi/extensions/zob-harness/src/runtime/state.ts +3 -0
- package/.pi/extensions/zob-harness/src/runtime/stop-restore.ts +144 -0
- package/.pi/extensions/zob-harness/src/runtime/tools-delegation/helpers.ts +23 -3
- package/.pi/extensions/zob-harness/src/runtime/tools-delegation/register.ts +34 -11
- package/.pi/extensions/zob-harness/src/types.ts +1 -0
- package/package.json +1 -1
|
@@ -52,6 +52,8 @@ export type { PlanLaunchStatus, PlanTodoCanonicalItem, PlanTodoCanonicalManifest
|
|
|
52
52
|
export { ZOB_COMPACTION_CONTINUITY_CONTRACT, ZOB_TOOL_ROUTING_CONTRACT } from "./src/core/constants.js";
|
|
53
53
|
export { ZOB_COMPACTION_DETAILS_SCHEMA, ZOB_COMPACTION_ENTRY_TYPE, ZOB_COMPACTION_HARD_CAP_TOKENS, ZOB_COMPACTION_LEDGER_SCHEMA, ZOB_COMPACTION_SUMMARY_SCHEMA, ZOB_COMPACTION_TARGET_TOKENS, buildDeterministicZobCompactionResult, buildDeterministicZobCompactionSummary, buildZobCompactionDetails, buildZobCompactionInstructions, buildZobCompactionLedgerEntry, buildZobCompactionStateCapsule, withZobCompactionDetails, zobCompactionBodyFreeViolations } from "./src/runtime/compaction-policy.js";
|
|
54
54
|
export type { ZobCompactionDetails, ZobCompactionFileRefsInput, ZobCompactionInstructionInput, ZobCompactionLedgerEntry, ZobCompactionStateCapsule } from "./src/runtime/compaction-policy.js";
|
|
55
|
+
export { assistantMessageHasVisibleOutput, createStopRestoreCandidate, findStopRestoreUserEntryId, markStopRestoreAssistantMessage, markStopRestoreRestored, markStopRestoreToolVisible, shouldRestoreStopPrompt } from "./src/runtime/stop-restore.js";
|
|
56
|
+
export type { StopRestoreCandidate, StopRestoreCandidateInput, StopRestoreDecision, StopRestoreDecisionInput, StopRestoreRewindResult } from "./src/runtime/stop-restore.js";
|
|
55
57
|
export { isAdaptiveZmodeAlias, renderAdaptiveZmodeTemplate, resolveAdaptiveZmodeEntrypoint, validateAdaptiveZmodeEntrypoint } from "./src/runtime/adaptive-zmode.js";
|
|
56
58
|
export type { AdaptiveZmodeAlias, AdaptiveZmodeEntrypoint } from "./src/runtime/adaptive-zmode.js";
|
|
57
59
|
export {
|
|
@@ -15,6 +15,7 @@ import type { HarnessRuntimeState } from "../state.js";
|
|
|
15
15
|
import { renderHarnessWidget } from "../widget.js";
|
|
16
16
|
import type { HarnessCommandContext } from "./types.js";
|
|
17
17
|
import { daemonInputFromState, daemonRuntimeLedgerEntry, stopDaemonLoop } from "./daemon.js";
|
|
18
|
+
import { findStopRestoreUserEntryId, markStopRestoreRestored, shouldRestoreStopPrompt, type StopRestoreDecision, type StopRestoreRewindResult } from "../stop-restore.js";
|
|
18
19
|
|
|
19
20
|
function abortForegroundWork(ctx: HarnessCommandContext): boolean {
|
|
20
21
|
const idle = typeof ctx.isIdle === "function" ? ctx.isIdle() : true;
|
|
@@ -52,6 +53,8 @@ function stopCommandLedgerEntry(input: {
|
|
|
52
53
|
daemonWasRunning: boolean;
|
|
53
54
|
runtimeGoalPaused: boolean;
|
|
54
55
|
runtimeGoalId?: string;
|
|
56
|
+
stopRestoreDecision?: StopRestoreDecision;
|
|
57
|
+
stopRestoreRewind?: StopRestoreRewindResult;
|
|
55
58
|
}): Record<string, unknown> {
|
|
56
59
|
return {
|
|
57
60
|
schema: "zob.stop-command.v1",
|
|
@@ -63,6 +66,14 @@ function stopCommandLedgerEntry(input: {
|
|
|
63
66
|
daemonWasRunning: input.daemonWasRunning,
|
|
64
67
|
runtimeGoalPaused: input.runtimeGoalPaused,
|
|
65
68
|
runtimeGoalId: input.runtimeGoalId,
|
|
69
|
+
editorPromptRestored: input.stopRestoreDecision?.restore === true,
|
|
70
|
+
editorPromptRestoreReason: input.stopRestoreDecision?.reason,
|
|
71
|
+
editorPromptHash: input.stopRestoreDecision?.promptHash,
|
|
72
|
+
assistantOutputObservedBeforeStop: input.stopRestoreDecision?.assistantOutputObserved,
|
|
73
|
+
editorPromptRewindAttempted: input.stopRestoreRewind?.attempted,
|
|
74
|
+
editorPromptRewindSucceeded: input.stopRestoreRewind?.succeeded,
|
|
75
|
+
editorPromptRewindReason: input.stopRestoreRewind?.reason,
|
|
76
|
+
editorPromptRewindTargetId: input.stopRestoreRewind?.targetEntryId,
|
|
66
77
|
bodyStored: false,
|
|
67
78
|
promptBodiesStored: false,
|
|
68
79
|
outputBodiesStored: false,
|
|
@@ -70,6 +81,36 @@ function stopCommandLedgerEntry(input: {
|
|
|
70
81
|
};
|
|
71
82
|
}
|
|
72
83
|
|
|
84
|
+
async function restoreStopPromptAndRewind(ctx: HarnessCommandContext, state: HarnessRuntimeState, decision: StopRestoreDecision): Promise<StopRestoreRewindResult> {
|
|
85
|
+
if (!decision.restore || !decision.promptText) {
|
|
86
|
+
return { attempted: false, succeeded: false, reason: decision.reason };
|
|
87
|
+
}
|
|
88
|
+
try {
|
|
89
|
+
if (typeof ctx.waitForIdle === "function") await ctx.waitForIdle();
|
|
90
|
+
} catch {
|
|
91
|
+
ctx.ui.setEditorText(decision.promptText);
|
|
92
|
+
markStopRestoreRestored(state.stopRestoreCandidate);
|
|
93
|
+
return { attempted: true, succeeded: false, reason: "wait_for_idle_failed" };
|
|
94
|
+
}
|
|
95
|
+
const targetEntryId = findStopRestoreUserEntryId(state.stopRestoreCandidate, ctx.sessionManager.getEntries());
|
|
96
|
+
if (!targetEntryId) {
|
|
97
|
+
ctx.ui.setEditorText(decision.promptText);
|
|
98
|
+
markStopRestoreRestored(state.stopRestoreCandidate);
|
|
99
|
+
return { attempted: true, succeeded: false, reason: "user_entry_not_found" };
|
|
100
|
+
}
|
|
101
|
+
try {
|
|
102
|
+
const navigation = await ctx.navigateTree(targetEntryId, { summarize: false });
|
|
103
|
+
ctx.ui.setEditorText(decision.promptText);
|
|
104
|
+
markStopRestoreRestored(state.stopRestoreCandidate);
|
|
105
|
+
if (navigation.cancelled) return { attempted: true, succeeded: false, reason: "navigate_cancelled", targetEntryId };
|
|
106
|
+
return { attempted: true, succeeded: true, reason: "rewound_to_prompt_checkpoint", targetEntryId };
|
|
107
|
+
} catch {
|
|
108
|
+
ctx.ui.setEditorText(decision.promptText);
|
|
109
|
+
markStopRestoreRestored(state.stopRestoreCandidate);
|
|
110
|
+
return { attempted: true, succeeded: false, reason: "navigate_failed", targetEntryId };
|
|
111
|
+
}
|
|
112
|
+
}
|
|
113
|
+
|
|
73
114
|
export function registerNewCommand(pi: ExtensionAPI, state: HarnessRuntimeState): void {
|
|
74
115
|
// Exact `/new` is handled by Pi before extension input/command hooks. Soft carryover
|
|
75
116
|
// is therefore written from the `session_shutdown` reason="new" hook in events.ts.
|
|
@@ -116,11 +157,17 @@ export function registerStopCommand(pi: ExtensionAPI, state: HarnessRuntimeState
|
|
|
116
157
|
const daemonWasRunning = state.daemon.loop.status === "running";
|
|
117
158
|
const runtimeGoalId = state.runtimeGoal?.goalId;
|
|
118
159
|
const foregroundAbortRequested = abortForegroundWork(ctx);
|
|
160
|
+
const stopRestoreDecision = shouldRestoreStopPrompt(state.stopRestoreCandidate, {
|
|
161
|
+
foregroundAbortRequested,
|
|
162
|
+
idleBeforeStop,
|
|
163
|
+
pendingMessagesBeforeStop,
|
|
164
|
+
});
|
|
119
165
|
const background = abortBackgroundDelegations(state);
|
|
120
166
|
stopDaemonLoop(state, "slash_stop");
|
|
121
167
|
const pausedGoal = pauseRuntimeGoalForStop(pi, state, "stopped by /stop; use /goal resume to continue");
|
|
122
168
|
const daemonState = buildDaemonRuntimeState(daemonInputFromState(state));
|
|
123
169
|
state.daemon.lastStatus = daemonState;
|
|
170
|
+
const stopRestoreRewind = await restoreStopPromptAndRewind(ctx, state, stopRestoreDecision);
|
|
124
171
|
pi.appendEntry("zob-daemon-runtime", daemonRuntimeLedgerEntry(daemonState));
|
|
125
172
|
pi.appendEntry("zob-stop", stopCommandLedgerEntry({
|
|
126
173
|
foregroundAbortRequested,
|
|
@@ -131,9 +178,11 @@ export function registerStopCommand(pi: ExtensionAPI, state: HarnessRuntimeState
|
|
|
131
178
|
daemonWasRunning,
|
|
132
179
|
runtimeGoalPaused: Boolean(pausedGoal && pausedGoal.goalId === runtimeGoalId && pausedGoal.status === "paused"),
|
|
133
180
|
runtimeGoalId,
|
|
181
|
+
stopRestoreDecision,
|
|
182
|
+
stopRestoreRewind,
|
|
134
183
|
}));
|
|
135
184
|
renderHarnessWidget(pi, state, ctx);
|
|
136
|
-
ctx.ui.notify(`ZOB stop: foreground=${foregroundAbortRequested ? "aborted" : "idle"} background_aborted=${background.abortedCount} daemon=${daemonWasRunning ? "stopped" : "already_stopped"} goal=${pausedGoal?.status ?? "none"}`, "warning");
|
|
185
|
+
ctx.ui.notify(`ZOB stop: foreground=${foregroundAbortRequested ? "aborted" : "idle"} input=${stopRestoreDecision.restore ? "restored" : "unchanged"} rewind=${stopRestoreRewind.succeeded ? "yes" : "no"} background_aborted=${background.abortedCount} daemon=${daemonWasRunning ? "stopped" : "already_stopped"} goal=${pausedGoal?.status ?? "none"}`, "warning");
|
|
137
186
|
},
|
|
138
187
|
});
|
|
139
188
|
}
|
|
@@ -3,7 +3,7 @@ import { resolve, sep } from "node:path";
|
|
|
3
3
|
import type { Theme } from "@earendil-works/pi-coding-agent";
|
|
4
4
|
import { Markdown, truncateToWidth, visibleWidth, type MarkdownTheme } from "@earendil-works/pi-tui";
|
|
5
5
|
|
|
6
|
-
import { delegationDurationMs, delegationSignalBadge, delegationSignalColor, formatDelegationContextLabel, formatDelegationCostLabel, formatDelegationModelLabel, formatDelegationSignalBadge, formatDuration, statusIcon, type DelegationRunView } from "./delegation-monitor.js";
|
|
6
|
+
import { delegationDurationMs, delegationSignalBadge, delegationSignalColor, formatDelegationContextLabel, formatDelegationCostLabel, formatDelegationCwdLabel, formatDelegationModelLabel, formatDelegationSignalBadge, formatDelegationWorkspaceLabel, formatDuration, statusIcon, type DelegationRunView } from "./delegation-monitor.js";
|
|
7
7
|
import { formatActivityDuration, formatActivitySummary, readDelegationActivitySnapshot } from "./delegation-activity.js";
|
|
8
8
|
import { sanitizeDelegationText } from "./delegation-markdown.js";
|
|
9
9
|
import { isRecord } from "../core/utils/records.js";
|
|
@@ -109,6 +109,7 @@ export function delegationFeedFingerprint(run: DelegationRunView | undefined, re
|
|
|
109
109
|
run.usage?.contextTokens ?? "",
|
|
110
110
|
(run.gateErrors ?? []).join(";"),
|
|
111
111
|
run.model ?? "",
|
|
112
|
+
run.cwd ?? "",
|
|
112
113
|
].join("|");
|
|
113
114
|
}
|
|
114
115
|
|
|
@@ -395,7 +396,10 @@ export function renderDelegationFeedLines(run: DelegationRunView | undefined, re
|
|
|
395
396
|
const signalBadge = delegationSignalBadge(run);
|
|
396
397
|
const signalText = formatDelegationSignalBadge(signalBadge);
|
|
397
398
|
const modelLabel = formatDelegationModelLabel(run);
|
|
398
|
-
|
|
399
|
+
const cwdLabel = formatDelegationCwdLabel(run, repoRoot);
|
|
400
|
+
const workspaceLabel = formatDelegationWorkspaceLabel(run, repoRoot, Math.max(24, safeWidth - 2));
|
|
401
|
+
lines.push(theme.fg("dim", `${statusIcon(run.status)} ${run.agent}${signalText ? ` · ${theme.fg(delegationSignalColor(signalBadge), signalText)}` : ""}${modelLabel ? ` · ${theme.fg("muted", `(${modelLabel})`)}` : ""}${cwdLabel ? ` · ${theme.fg("muted", cwdLabel)}` : ""} · ${run.status}${run.failureKind ? ` · ${run.failureKind}` : ""} · ${formatDuration(delegationDurationMs(run))} · ${formatDelegationCostLabel(run)} · ${formatDelegationContextLabel(run)}`));
|
|
402
|
+
if (workspaceLabel) lines.push(theme.fg("accent", workspaceLabel));
|
|
399
403
|
if (run.usage) lines.push(theme.fg("muted", `usage · in ${run.usage.input} · out ${run.usage.output} · cache ${run.usage.cacheRead}/${run.usage.cacheWrite} · context ${run.usage.contextTokens}`));
|
|
400
404
|
if (run.taskPreview) lines.push(theme.fg("muted", `task · ${sanitizeDelegationText(run.taskPreview)}`));
|
|
401
405
|
renderLiveActivityCard(run, repoRoot, lines, safeWidth, theme);
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { closeSync, existsSync, openSync, readFileSync, readSync, statSync } from "node:fs";
|
|
2
|
-
import { resolve, sep } from "node:path";
|
|
2
|
+
import { relative, resolve, sep } from "node:path";
|
|
3
3
|
|
|
4
4
|
import type { ChildResult, DelegationFailureKind } from "../types.js";
|
|
5
5
|
import { isRecord } from "../core/utils/records.js";
|
|
@@ -29,6 +29,7 @@ export interface DelegationRunView {
|
|
|
29
29
|
endedAtMs?: number;
|
|
30
30
|
outputPreview: string;
|
|
31
31
|
stderrPreview: string;
|
|
32
|
+
cwd?: string;
|
|
32
33
|
sessionPath?: string;
|
|
33
34
|
exitCode?: number;
|
|
34
35
|
gatePassed?: boolean;
|
|
@@ -255,6 +256,29 @@ export function formatDelegationModelLabel(run: DelegationRunView | { model?: st
|
|
|
255
256
|
return value.length <= limit ? value : `${value.slice(0, limit - 1)}…`;
|
|
256
257
|
}
|
|
257
258
|
|
|
259
|
+
function delegationCwdValue(run: DelegationRunView | ChildResult | { cwd?: string } | undefined, repoRoot: string): string | undefined {
|
|
260
|
+
if (!run?.cwd) return undefined;
|
|
261
|
+
const resolvedRoot = resolve(repoRoot);
|
|
262
|
+
const resolvedCwd = resolve(run.cwd);
|
|
263
|
+
const rel = resolvedCwd === resolvedRoot ? "." : relative(resolvedRoot, resolvedCwd);
|
|
264
|
+
const inside = rel === "" || (rel !== ".." && !rel.startsWith(`..${sep}`) && !rel.startsWith(sep));
|
|
265
|
+
return inside ? rel || "." : resolvedCwd;
|
|
266
|
+
}
|
|
267
|
+
|
|
268
|
+
function truncateLabel(label: string, limit: number): string {
|
|
269
|
+
return label.length <= limit ? label : `${label.slice(0, limit - 1)}…`;
|
|
270
|
+
}
|
|
271
|
+
|
|
272
|
+
export function formatDelegationCwdLabel(run: DelegationRunView | ChildResult | { cwd?: string } | undefined, repoRoot: string, limit = 48): string {
|
|
273
|
+
const value = delegationCwdValue(run, repoRoot);
|
|
274
|
+
return value ? truncateLabel(`cwd ${value}`, limit) : "";
|
|
275
|
+
}
|
|
276
|
+
|
|
277
|
+
export function formatDelegationWorkspaceLabel(run: DelegationRunView | ChildResult | { cwd?: string } | undefined, repoRoot: string, limit = 72): string {
|
|
278
|
+
const value = delegationCwdValue(run, repoRoot);
|
|
279
|
+
return value ? truncateLabel(`workspace · ${value}`, limit) : "";
|
|
280
|
+
}
|
|
281
|
+
|
|
258
282
|
function stripSignalControlSequences(text: string): string {
|
|
259
283
|
return text
|
|
260
284
|
.replace(/\x1b\][\s\S]*?(?:\x07|\x1b\\)/g, "")
|
|
@@ -429,6 +453,7 @@ export function startDelegationRun(state: DelegationMonitorState, input: {
|
|
|
429
453
|
agent: string;
|
|
430
454
|
task: string;
|
|
431
455
|
startedAtMs: number;
|
|
456
|
+
cwd?: string;
|
|
432
457
|
}): DelegationRunView {
|
|
433
458
|
const existingIndex = state.runs.findIndex((run) => run.id === input.id);
|
|
434
459
|
const run: DelegationRunView = {
|
|
@@ -443,6 +468,7 @@ export function startDelegationRun(state: DelegationMonitorState, input: {
|
|
|
443
468
|
startedAtMs: input.startedAtMs,
|
|
444
469
|
outputPreview: "",
|
|
445
470
|
stderrPreview: "",
|
|
471
|
+
cwd: input.cwd,
|
|
446
472
|
};
|
|
447
473
|
if (existingIndex >= 0) state.runs[existingIndex] = run;
|
|
448
474
|
else state.runs.push(run);
|
|
@@ -463,6 +489,7 @@ export function updateDelegationRun(state: DelegationMonitorState, id: string, p
|
|
|
463
489
|
if (patch.endedAtMs !== undefined) run.endedAtMs = patch.endedAtMs;
|
|
464
490
|
if (patch.outputPreview !== undefined) run.outputPreview = capPreview(patch.outputPreview);
|
|
465
491
|
if (patch.stderrPreview !== undefined) run.stderrPreview = capPreview(patch.stderrPreview);
|
|
492
|
+
if (patch.cwd !== undefined) run.cwd = patch.cwd;
|
|
466
493
|
if (patch.sessionPath !== undefined) run.sessionPath = patch.sessionPath;
|
|
467
494
|
if (patch.exitCode !== undefined) run.exitCode = patch.exitCode;
|
|
468
495
|
if (patch.gatePassed !== undefined) run.gatePassed = patch.gatePassed;
|
|
@@ -539,6 +566,7 @@ export function buildDelegationLogLines(run: DelegationRunView | undefined, repo
|
|
|
539
566
|
`[delegation ${run.id}]`,
|
|
540
567
|
`agent: ${run.agent}`,
|
|
541
568
|
run.model ? `model: ${run.model}` : undefined,
|
|
569
|
+
formatDelegationWorkspaceLabel(run, repoRoot) || undefined,
|
|
542
570
|
`status: ${run.status}`,
|
|
543
571
|
`duration: ${formatDuration(delegationDurationMs(run))}`,
|
|
544
572
|
run.sessionPath ? `session: ${run.sessionPath}` : "session: not captured yet",
|
|
@@ -2,7 +2,7 @@ import type { ExtensionContext, Theme } from "@earendil-works/pi-coding-agent";
|
|
|
2
2
|
import { matchesKey, truncateToWidth, visibleWidth, type Component, type TUI } from "@earendil-works/pi-tui";
|
|
3
3
|
|
|
4
4
|
import type { DelegationMonitorState, DelegationRunView, DelegationSortMode } from "./delegation-monitor.js";
|
|
5
|
-
import { buildDelegationGroups, delegationCost, delegationDurationMs, delegationSignalBadge, delegationSignalColor, formatDelegationContextLabel, formatDelegationCost, formatDelegationCostLabel, formatDelegationModelLabel, formatDelegationSignalBadge, formatDuration, statusIcon } from "./delegation-monitor.js";
|
|
5
|
+
import { buildDelegationGroups, delegationCost, delegationDurationMs, delegationSignalBadge, delegationSignalColor, formatDelegationContextLabel, formatDelegationCost, formatDelegationCostLabel, formatDelegationCwdLabel, formatDelegationModelLabel, formatDelegationSignalBadge, formatDelegationWorkspaceLabel, formatDuration, statusIcon } from "./delegation-monitor.js";
|
|
6
6
|
import { delegationFeedFingerprint, renderDelegationFeedLines } from "./delegation-feed.js";
|
|
7
7
|
import { delegateCloseButton, delegateSelectMarker } from "./delegation-click-markers.js";
|
|
8
8
|
import { disableDelegationMouseMode, enableDelegationMouseMode, handleDelegationMouseInput } from "./delegation-mouse.js";
|
|
@@ -247,8 +247,9 @@ export class DelegationOverlayComponent implements Component {
|
|
|
247
247
|
const headerLeft = `${th.fg(this.activePane === "list" ? "accent" : "muted", this.activePane === "list" ? "▶ Agents" : " Agents")} ${th.fg("muted", delegateCloseButton())}`;
|
|
248
248
|
const selectedBadge = delegationSignalBadge(selected);
|
|
249
249
|
const selectedBadgeText = formatDelegationSignalBadge(selectedBadge);
|
|
250
|
+
const selectedWorkspaceLabel = formatDelegationWorkspaceLabel(selected, this.repoRoot, 42);
|
|
250
251
|
const selectedTitle = selected
|
|
251
|
-
? `${th.fg(statusColor(selected.status), `${statusIcon(selected.status)} ${selected.agent}`)}${selectedBadgeText ? ` ${th.fg(delegationSignalColor(selectedBadge), selectedBadgeText)}` : ""}${formatDelegationModelLabel(selected) ? ` ${th.fg("muted", `(${formatDelegationModelLabel(selected)})`)}` : ""} ${th.fg("dim", formatDuration(delegationDurationMs(selected)))} ${th.fg("accent", formatDelegationCostLabel(selected))} ${th.fg("muted", formatDelegationContextLabel(selected))}`
|
|
252
|
+
? `${th.fg(statusColor(selected.status), `${statusIcon(selected.status)} ${selected.agent}`)}${selectedBadgeText ? ` ${th.fg(delegationSignalColor(selectedBadge), selectedBadgeText)}` : ""}${formatDelegationModelLabel(selected) ? ` ${th.fg("muted", `(${formatDelegationModelLabel(selected)})`)}` : ""}${selectedWorkspaceLabel ? ` ${th.fg("muted", selectedWorkspaceLabel)}` : ""} ${th.fg("dim", formatDuration(delegationDurationMs(selected)))} ${th.fg("accent", formatDelegationCostLabel(selected))} ${th.fg("muted", formatDelegationContextLabel(selected))}`
|
|
252
253
|
: th.fg("warning", "No delegation selected");
|
|
253
254
|
const headerRight = `${th.fg(this.activePane === "feed" ? "accent" : "muted", this.activePane === "feed" ? "▶ Feed" : " Feed")} ${selectedTitle}`;
|
|
254
255
|
lines.push(this.row(padToWidth(headerLeft, listWidth) + th.fg("dim", "│") + padToWidth(headerRight, logWidth), inner));
|
|
@@ -432,7 +433,8 @@ export class DelegationOverlayComponent implements Component {
|
|
|
432
433
|
const badge = formatDelegationSignalBadge(delegationSignalBadge(run));
|
|
433
434
|
const modelLabel = formatDelegationModelLabel(run);
|
|
434
435
|
const modelSuffix = modelLabel ? ` (${modelLabel})` : "";
|
|
435
|
-
const
|
|
436
|
+
const cwdSuffix = formatDelegationCwdLabel(run, this.repoRoot, 28);
|
|
437
|
+
const base = `${row.label}${badge ? ` ${badge}` : ""}${modelSuffix}${cwdSuffix ? ` ${cwdSuffix}` : ""} ${duration} ${cost} ${context} [view]`;
|
|
436
438
|
const labeled = `${truncateToWidth(base, width - (selected ? 2 : 0), "…")}${delegateSelectMarker(run.id)}`;
|
|
437
439
|
const colored = th.fg(statusColor(run.status), labeled);
|
|
438
440
|
return selected ? th.bg("selectedBg", padToWidth(colored, width)) : colored;
|
|
@@ -39,6 +39,7 @@ import { extractModeIntent, stripModeIntentMarkup, validateModeIntent, type ZobM
|
|
|
39
39
|
import { capturePlanArtifact } from "./plan-capture.js";
|
|
40
40
|
import { redactPlanTodosBlockForDisplay } from "../domains/plan/plan-todos.js";
|
|
41
41
|
import { applyMode, renderHarnessWidget } from "./widget.js";
|
|
42
|
+
import { createStopRestoreCandidate, markStopRestoreAssistantMessage, markStopRestoreToolVisible } from "./stop-restore.js";
|
|
42
43
|
|
|
43
44
|
function safelyUpdateZobLivePeer(repoRoot: string, action: "register" | "touch" | "unregister"): void {
|
|
44
45
|
try {
|
|
@@ -937,6 +938,15 @@ export function registerHarnessEvents(pi: ExtensionAPI, state: HarnessRuntimeSta
|
|
|
937
938
|
return { action: "continue" as const };
|
|
938
939
|
}
|
|
939
940
|
state.lastUserInputText = event.text;
|
|
941
|
+
const streamingBehavior = (event as { streamingBehavior?: unknown }).streamingBehavior;
|
|
942
|
+
const stopRestoreCandidate = createStopRestoreCandidate({
|
|
943
|
+
text: event.text,
|
|
944
|
+
source: event.source,
|
|
945
|
+
streamingBehavior: streamingBehavior === "steer" || streamingBehavior === "followUp" ? streamingBehavior : undefined,
|
|
946
|
+
leafId: ctx.sessionManager.getLeafId(),
|
|
947
|
+
});
|
|
948
|
+
if (stopRestoreCandidate) state.stopRestoreCandidate = stopRestoreCandidate;
|
|
949
|
+
else if (!streamingBehavior) state.stopRestoreCandidate = undefined;
|
|
940
950
|
if (!event.text.trim().startsWith("/") && state.autonomy.enabled) {
|
|
941
951
|
const readiness = scoreMissionReadiness(event.text, { mode: state.autonomy.mode, policy: state.autonomy.policy });
|
|
942
952
|
state.autonomy.lastReadiness = readiness;
|
|
@@ -1002,6 +1012,36 @@ export function registerHarnessEvents(pi: ExtensionAPI, state: HarnessRuntimeSta
|
|
|
1002
1012
|
return { action: "continue" as const };
|
|
1003
1013
|
});
|
|
1004
1014
|
|
|
1015
|
+
pi.on("message_start", async (event, ctx) => {
|
|
1016
|
+
if (!isRecord(event.message)) return undefined;
|
|
1017
|
+
if (event.message.role === "user") {
|
|
1018
|
+
const text = textFromMessage(event.message as AssistantLikeMessage);
|
|
1019
|
+
const stopRestoreCandidate = createStopRestoreCandidate({
|
|
1020
|
+
text,
|
|
1021
|
+
source: "interactive",
|
|
1022
|
+
leafId: ctx.sessionManager.getLeafId(),
|
|
1023
|
+
});
|
|
1024
|
+
if (stopRestoreCandidate) state.stopRestoreCandidate = stopRestoreCandidate;
|
|
1025
|
+
return undefined;
|
|
1026
|
+
}
|
|
1027
|
+
if (event.message.role === "assistant") {
|
|
1028
|
+
markStopRestoreAssistantMessage(state.stopRestoreCandidate, event.message as AssistantLikeMessage);
|
|
1029
|
+
}
|
|
1030
|
+
return undefined;
|
|
1031
|
+
});
|
|
1032
|
+
|
|
1033
|
+
pi.on("message_update", async (event) => {
|
|
1034
|
+
if (isRecord(event.message) && event.message.role === "assistant") {
|
|
1035
|
+
markStopRestoreAssistantMessage(state.stopRestoreCandidate, event.message as AssistantLikeMessage);
|
|
1036
|
+
}
|
|
1037
|
+
return undefined;
|
|
1038
|
+
});
|
|
1039
|
+
|
|
1040
|
+
pi.on("tool_execution_start", async () => {
|
|
1041
|
+
markStopRestoreToolVisible(state.stopRestoreCandidate);
|
|
1042
|
+
return undefined;
|
|
1043
|
+
});
|
|
1044
|
+
|
|
1005
1045
|
pi.on("tool_call", async (event, ctx) => {
|
|
1006
1046
|
if (state.activeMode === "vanilla") return { action: "continue" as const };
|
|
1007
1047
|
|
|
@@ -1072,6 +1112,7 @@ export function registerHarnessEvents(pi: ExtensionAPI, state: HarnessRuntimeSta
|
|
|
1072
1112
|
|
|
1073
1113
|
pi.on("message_end", async (event, ctx) => {
|
|
1074
1114
|
if (!isRecord(event.message) || event.message.role !== "assistant") return undefined;
|
|
1115
|
+
markStopRestoreAssistantMessage(state.stopRestoreCandidate, event.message as AssistantLikeMessage);
|
|
1075
1116
|
const text = textFromMessage(event.message as AssistantLikeMessage);
|
|
1076
1117
|
const visibleText = stripModeIntentMarkup(text);
|
|
1077
1118
|
let capturedPlanPath: string | undefined;
|
|
@@ -45,6 +45,7 @@ const TaskItem = Type.Object({
|
|
|
45
45
|
const DelegateParams = Type.Object({
|
|
46
46
|
agent: Type.Optional(Type.String({ description: "Agent name for single-agent mode" })),
|
|
47
47
|
task: Type.Optional(Type.String({ description: "Task for single-agent mode" })),
|
|
48
|
+
cwd: Type.Optional(Type.String({ description: "Default cwd for delegate_agent child Pi processes. Must stay inside repo; tasks[].cwd and chain[].cwd override this value." })),
|
|
48
49
|
tasks: Type.Optional(Type.Array(TaskItem, { description: "Parallel tasks. Max 8, 4 concurrent." })),
|
|
49
50
|
chain: Type.Optional(Type.Array(TaskItem, { description: "Sequential chain. {previous} is replaced by prior output." })),
|
|
50
51
|
scope: Type.Optional(AgentScopeSchema),
|
|
@@ -19,6 +19,7 @@ import type { ZobModeIntent } from "./mode-intent.js";
|
|
|
19
19
|
import type { ZAgentRoomBinding } from "../domains/coms/zagents.js";
|
|
20
20
|
import { createZcompactRuntimeState, restoreZcompactStateFromBranch, type ZcompactRuntimeState } from "./auto-compaction.js";
|
|
21
21
|
import { createZcommitRuntimeState, recordZcommitOwnedPath, type ZcommitLastCommitRecord, type ZcommitOwnershipSource, type ZcommitRuntimeState, type ZcommitToggleState } from "../domains/git/git-ops.js";
|
|
22
|
+
import type { StopRestoreCandidate } from "./stop-restore.js";
|
|
22
23
|
|
|
23
24
|
export interface DelegationMouseRuntimeState {
|
|
24
25
|
tui?: TUI;
|
|
@@ -153,6 +154,7 @@ export interface HarnessRuntimeState {
|
|
|
153
154
|
runtimeGoalLastAccountedAtMs?: number;
|
|
154
155
|
activeRuleResolution?: RuleResolution;
|
|
155
156
|
lastUserInputText?: string;
|
|
157
|
+
stopRestoreCandidate?: StopRestoreCandidate;
|
|
156
158
|
lastModeIntent?: ZobModeIntent & { at: number; accepted: boolean; validationReason: string };
|
|
157
159
|
delegations: DelegationMonitorState;
|
|
158
160
|
delegationMouse: DelegationMouseRuntimeState;
|
|
@@ -181,6 +183,7 @@ export function createHarnessRuntimeState(): HarnessRuntimeState {
|
|
|
181
183
|
runtimeGoalLastAccountedAtMs: undefined,
|
|
182
184
|
activeRuleResolution: undefined,
|
|
183
185
|
lastUserInputText: undefined,
|
|
186
|
+
stopRestoreCandidate: undefined,
|
|
184
187
|
lastModeIntent: undefined,
|
|
185
188
|
delegations: createDelegationMonitorState(),
|
|
186
189
|
delegationMouse: { enabled: false, opening: false, mouseReleaseEpoch: 0 },
|
|
@@ -0,0 +1,144 @@
|
|
|
1
|
+
import type { AssistantLikeMessage } from "../types.js";
|
|
2
|
+
import { sha256 } from "../core/utils/hashing.js";
|
|
3
|
+
import { isRecord } from "../core/utils/records.js";
|
|
4
|
+
|
|
5
|
+
export type StopRestoreInputSource = "interactive" | "rpc";
|
|
6
|
+
export type StopRestoreStreamingBehavior = "steer" | "followUp";
|
|
7
|
+
|
|
8
|
+
export interface StopRestoreCandidate {
|
|
9
|
+
schema: "zob.stop-restore-candidate.v1";
|
|
10
|
+
promptText: string;
|
|
11
|
+
promptHash: string;
|
|
12
|
+
source: StopRestoreInputSource;
|
|
13
|
+
inputAtMs: number;
|
|
14
|
+
leafId: string | null;
|
|
15
|
+
assistantStarted: boolean;
|
|
16
|
+
assistantVisibleOutput: boolean;
|
|
17
|
+
toolVisibleOutput: boolean;
|
|
18
|
+
restoredAtMs?: number;
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
export interface StopRestoreCandidateInput {
|
|
22
|
+
text: string;
|
|
23
|
+
source: string;
|
|
24
|
+
streamingBehavior?: StopRestoreStreamingBehavior;
|
|
25
|
+
leafId?: string | null;
|
|
26
|
+
nowMs?: number;
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
export interface StopRestoreDecisionInput {
|
|
30
|
+
foregroundAbortRequested: boolean;
|
|
31
|
+
idleBeforeStop: boolean;
|
|
32
|
+
pendingMessagesBeforeStop: boolean;
|
|
33
|
+
nowMs?: number;
|
|
34
|
+
maxCandidateAgeMs?: number;
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
export interface StopRestoreDecision {
|
|
38
|
+
restore: boolean;
|
|
39
|
+
reason: string;
|
|
40
|
+
promptText?: string;
|
|
41
|
+
promptHash?: string;
|
|
42
|
+
assistantOutputObserved: boolean;
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
export interface StopRestoreRewindResult {
|
|
46
|
+
attempted: boolean;
|
|
47
|
+
succeeded: boolean;
|
|
48
|
+
reason: string;
|
|
49
|
+
targetEntryId?: string;
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
const DEFAULT_MAX_CANDIDATE_AGE_MS = 30 * 60 * 1000;
|
|
53
|
+
|
|
54
|
+
function restorablePromptText(text: string): boolean {
|
|
55
|
+
const trimmed = text.trim();
|
|
56
|
+
return trimmed.length > 0 && !trimmed.startsWith("/");
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
export function createStopRestoreCandidate(input: StopRestoreCandidateInput): StopRestoreCandidate | undefined {
|
|
60
|
+
if (input.source !== "interactive" && input.source !== "rpc") return undefined;
|
|
61
|
+
if (input.streamingBehavior) return undefined;
|
|
62
|
+
if (!restorablePromptText(input.text)) return undefined;
|
|
63
|
+
return {
|
|
64
|
+
schema: "zob.stop-restore-candidate.v1",
|
|
65
|
+
promptText: input.text,
|
|
66
|
+
promptHash: sha256(input.text),
|
|
67
|
+
source: input.source,
|
|
68
|
+
inputAtMs: input.nowMs ?? Date.now(),
|
|
69
|
+
leafId: input.leafId ?? null,
|
|
70
|
+
assistantStarted: false,
|
|
71
|
+
assistantVisibleOutput: false,
|
|
72
|
+
toolVisibleOutput: false,
|
|
73
|
+
};
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
export function assistantMessageHasVisibleOutput(message: AssistantLikeMessage | undefined): boolean {
|
|
77
|
+
if (!message || !Array.isArray(message.content)) return false;
|
|
78
|
+
return message.content.some((part) => {
|
|
79
|
+
if (!isRecord(part)) return false;
|
|
80
|
+
if (part.type === "text") return typeof part.text === "string" && part.text.trim().length > 0;
|
|
81
|
+
if (part.type === "thinking") return typeof part.thinking === "string" && part.thinking.trim().length > 0;
|
|
82
|
+
if (part.type === "toolCall") return true;
|
|
83
|
+
return false;
|
|
84
|
+
});
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
export function markStopRestoreAssistantMessage(candidate: StopRestoreCandidate | undefined, message: AssistantLikeMessage | undefined): void {
|
|
88
|
+
if (!candidate || candidate.restoredAtMs) return;
|
|
89
|
+
candidate.assistantStarted = true;
|
|
90
|
+
if (assistantMessageHasVisibleOutput(message)) candidate.assistantVisibleOutput = true;
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
export function markStopRestoreToolVisible(candidate: StopRestoreCandidate | undefined): void {
|
|
94
|
+
if (!candidate || candidate.restoredAtMs) return;
|
|
95
|
+
candidate.toolVisibleOutput = true;
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
function textFromEntryContent(content: unknown): string {
|
|
99
|
+
if (typeof content === "string") return content;
|
|
100
|
+
if (!Array.isArray(content)) return "";
|
|
101
|
+
return content
|
|
102
|
+
.filter((part): part is { type: string; text: string } => isRecord(part) && part.type === "text" && typeof part.text === "string")
|
|
103
|
+
.map((part) => part.text)
|
|
104
|
+
.join("");
|
|
105
|
+
}
|
|
106
|
+
|
|
107
|
+
export function findStopRestoreUserEntryId(candidate: StopRestoreCandidate | undefined, entries: unknown[]): string | undefined {
|
|
108
|
+
if (!candidate) return undefined;
|
|
109
|
+
for (let index = entries.length - 1; index >= 0; index -= 1) {
|
|
110
|
+
const entry = entries[index];
|
|
111
|
+
if (!isRecord(entry) || entry.type !== "message" || typeof entry.id !== "string") continue;
|
|
112
|
+
const parentId = typeof entry.parentId === "string" ? entry.parentId : entry.parentId === null ? null : undefined;
|
|
113
|
+
if (parentId !== candidate.leafId) continue;
|
|
114
|
+
if (!isRecord(entry.message) || entry.message.role !== "user") continue;
|
|
115
|
+
if (textFromEntryContent(entry.message.content) === candidate.promptText) return entry.id;
|
|
116
|
+
}
|
|
117
|
+
return undefined;
|
|
118
|
+
}
|
|
119
|
+
|
|
120
|
+
export function markStopRestoreRestored(candidate: StopRestoreCandidate | undefined, nowMs = Date.now()): void {
|
|
121
|
+
if (!candidate) return;
|
|
122
|
+
candidate.restoredAtMs = nowMs;
|
|
123
|
+
}
|
|
124
|
+
|
|
125
|
+
export function shouldRestoreStopPrompt(candidate: StopRestoreCandidate | undefined, input: StopRestoreDecisionInput): StopRestoreDecision {
|
|
126
|
+
const assistantOutputObserved = Boolean(candidate?.assistantVisibleOutput || candidate?.toolVisibleOutput);
|
|
127
|
+
if (!candidate) return { restore: false, reason: "no_candidate", assistantOutputObserved };
|
|
128
|
+
if (!input.foregroundAbortRequested) return { restore: false, reason: "foreground_idle", assistantOutputObserved };
|
|
129
|
+
if (input.idleBeforeStop) return { restore: false, reason: "idle_before_stop", assistantOutputObserved };
|
|
130
|
+
if (input.pendingMessagesBeforeStop) return { restore: false, reason: "pending_messages_restored_by_pi", assistantOutputObserved };
|
|
131
|
+
if (candidate.restoredAtMs) return { restore: false, reason: "already_restored", promptHash: candidate.promptHash, assistantOutputObserved };
|
|
132
|
+
if (!restorablePromptText(candidate.promptText)) return { restore: false, reason: "non_restorable_prompt", promptHash: candidate.promptHash, assistantOutputObserved };
|
|
133
|
+
if (assistantOutputObserved) return { restore: false, reason: "assistant_output_observed", promptHash: candidate.promptHash, assistantOutputObserved };
|
|
134
|
+
const nowMs = input.nowMs ?? Date.now();
|
|
135
|
+
const maxAgeMs = input.maxCandidateAgeMs ?? DEFAULT_MAX_CANDIDATE_AGE_MS;
|
|
136
|
+
if (nowMs - candidate.inputAtMs > maxAgeMs) return { restore: false, reason: "candidate_stale", promptHash: candidate.promptHash, assistantOutputObserved };
|
|
137
|
+
return {
|
|
138
|
+
restore: true,
|
|
139
|
+
reason: "foreground_aborted_before_assistant_output",
|
|
140
|
+
promptText: candidate.promptText,
|
|
141
|
+
promptHash: candidate.promptHash,
|
|
142
|
+
assistantOutputObserved,
|
|
143
|
+
};
|
|
144
|
+
}
|
|
@@ -32,7 +32,9 @@ import {
|
|
|
32
32
|
delegationSignalColor,
|
|
33
33
|
extractDelegationSignalBadge,
|
|
34
34
|
finishDelegationRun,
|
|
35
|
+
formatDelegationCwdLabel,
|
|
35
36
|
formatDelegationModelLabel,
|
|
37
|
+
formatDelegationWorkspaceLabel,
|
|
36
38
|
formatDelegationSignalBadge,
|
|
37
39
|
formatDuration,
|
|
38
40
|
hasActiveDelegations,
|
|
@@ -625,6 +627,7 @@ export function hydrateDelegationRunsFromDetails(source: "delegate_agent" | "del
|
|
|
625
627
|
agent: result.agent,
|
|
626
628
|
task: result.task || "restored from delegate tool result",
|
|
627
629
|
startedAtMs: nowMs - Math.max(1, index + 1),
|
|
630
|
+
cwd: result.cwd,
|
|
628
631
|
});
|
|
629
632
|
}
|
|
630
633
|
finishDelegationRun(state.delegations, result.ledgerRunId, {
|
|
@@ -634,6 +637,7 @@ export function hydrateDelegationRunsFromDetails(source: "delegate_agent" | "del
|
|
|
634
637
|
index,
|
|
635
638
|
agent: result.agent,
|
|
636
639
|
model: result.model,
|
|
640
|
+
cwd: result.cwd,
|
|
637
641
|
status: result.stopReason === "aborted" ? "aborted" : isFailed(result) ? "failed" : "complete",
|
|
638
642
|
endedAtMs: existing?.endedAtMs ?? nowMs,
|
|
639
643
|
outputPreview: result.output,
|
|
@@ -730,6 +734,16 @@ export function formatDelegationCatalogSummary(catalog: Record<string, unknown>)
|
|
|
730
734
|
return lines.join("\n");
|
|
731
735
|
}
|
|
732
736
|
|
|
737
|
+
function formatWorkspaceSummaryLine(items: Array<DelegationRunView | ChildResult>, repoRoot: string): string {
|
|
738
|
+
const workspaces = [...new Set(items.map((item) => formatDelegationWorkspaceLabel(item, repoRoot, 72)).filter(Boolean))];
|
|
739
|
+
if (workspaces.length === 0) return "";
|
|
740
|
+
if (workspaces.length === 1) return workspaces[0] ?? "";
|
|
741
|
+
const values = workspaces.map((label) => label.replace(/^workspace · /, ""));
|
|
742
|
+
const visible = values.slice(0, 3).join(", ");
|
|
743
|
+
const suffix = values.length > 3 ? `, +${values.length - 3} more` : "";
|
|
744
|
+
return `workspaces · ${visible}${suffix}`;
|
|
745
|
+
}
|
|
746
|
+
|
|
733
747
|
function renderMiniActivityLines(run: DelegationRunView, nowMs: number, expanded: boolean, theme: { fg: (color: any, text: string) => string }): string[] {
|
|
734
748
|
const lines: string[] = [];
|
|
735
749
|
const snapshot = readDelegationActivitySnapshot(run.sessionPath, process.cwd(), nowMs);
|
|
@@ -770,8 +784,10 @@ export function renderDelegationToolResultText(source: "delegate_agent" | "deleg
|
|
|
770
784
|
const mode = details?.mode ?? (source === "delegate_task" ? "single" : "single");
|
|
771
785
|
const lines = [
|
|
772
786
|
`${theme.fg("toolTitle", theme.bold(source))} ${theme.fg("accent", mode)} ${isPartial ? theme.fg("warning", "running") : theme.fg(failCount > 0 ? "error" : "success", `${okCount}/${results.length || monitoredRuns.length || 1} ok`)}`,
|
|
773
|
-
`${theme.fg("dim", "running")} ${theme.fg(runningCount > 0 ? "warning" : "dim", String(runningCount))} ${theme.fg("dim", "blocked")} ${theme.fg(blockedCount > 0 ? "warning" : "dim", String(blockedCount))} ${theme.fg("dim", "gate")} ${theme.fg(gateCount > 0 ? "warning" : "dim", String(gateCount))} ${theme.fg("dim", "runtime")} ${theme.fg(runtimeCount > 0 ? "error" : "dim", String(runtimeCount))} ${theme.fg("dim", "details")} ${theme.fg("muted", "/zstatus delegations")}`,
|
|
774
787
|
];
|
|
788
|
+
const workspaceSummary = formatWorkspaceSummaryLine(monitoredRuns.length > 0 ? monitoredRuns : results, process.cwd());
|
|
789
|
+
if (workspaceSummary) lines.push(theme.fg("accent", workspaceSummary));
|
|
790
|
+
lines.push(`${theme.fg("dim", "running")} ${theme.fg(runningCount > 0 ? "warning" : "dim", String(runningCount))} ${theme.fg("dim", "blocked")} ${theme.fg(blockedCount > 0 ? "warning" : "dim", String(blockedCount))} ${theme.fg("dim", "gate")} ${theme.fg(gateCount > 0 ? "warning" : "dim", String(gateCount))} ${theme.fg("dim", "runtime")} ${theme.fg(runtimeCount > 0 ? "error" : "dim", String(runtimeCount))} ${theme.fg("dim", "details")} ${theme.fg("muted", "/zstatus delegations")}`);
|
|
775
791
|
|
|
776
792
|
if (monitoredRuns.length > 0) {
|
|
777
793
|
const maxRows = expanded ? 12 : 5;
|
|
@@ -787,7 +803,9 @@ export function renderDelegationToolResultText(source: "delegate_agent" | "deleg
|
|
|
787
803
|
const badgeText = formatDelegationSignalBadge(badge);
|
|
788
804
|
const modelLabel = formatDelegationModelLabel(run);
|
|
789
805
|
const modelSuffix = modelLabel ? ` ${theme.fg("muted", `(${modelLabel})`)}` : "";
|
|
790
|
-
|
|
806
|
+
const cwdLabel = formatDelegationCwdLabel(run, process.cwd(), 36);
|
|
807
|
+
const cwdSuffix = cwdLabel ? ` ${theme.fg("muted", cwdLabel)}` : "";
|
|
808
|
+
lines.push(`${theme.fg("dim", prefix)} ${theme.fg(color, `${statusIcon(run.status)} ${run.agent}${kind}${background}`)}${badgeText ? ` ${theme.fg(delegationSignalColor(badge), badgeText)}` : ""}${modelSuffix}${cwdSuffix} ${theme.fg("dim", formatDuration(delegationDurationMs(run, nowMs)))} ${theme.fg("muted", viewHint)}`);
|
|
791
809
|
lines.push(...renderMiniActivityLines(run, nowMs, expanded, theme));
|
|
792
810
|
if (expanded && (run.errorMessage || run.stopReason || run.gateErrors?.length)) lines.push(` ${theme.fg("dim", run.errorMessage ?? run.gateErrors?.join("; ") ?? run.stopReason ?? "")}`);
|
|
793
811
|
}
|
|
@@ -808,7 +826,9 @@ export function renderDelegationToolResultText(source: "delegate_agent" | "deleg
|
|
|
808
826
|
const badgeText = formatDelegationSignalBadge(badge);
|
|
809
827
|
const modelLabel = formatDelegationModelLabel(result);
|
|
810
828
|
const modelSuffix = modelLabel ? ` ${theme.fg("muted", `(${modelLabel})`)}` : "";
|
|
811
|
-
|
|
829
|
+
const cwdLabel = formatDelegationCwdLabel(result, process.cwd(), 36);
|
|
830
|
+
const cwdSuffix = cwdLabel ? ` ${theme.fg("muted", cwdLabel)}` : "";
|
|
831
|
+
lines.push(`${theme.fg("dim", prefix)} ${theme.fg(color, `${failed ? "✗" : "✓"} ${result.agent}${kind}`)}${badgeText ? ` ${theme.fg(delegationSignalColor(badge), badgeText)}` : ""}${modelSuffix}${cwdSuffix} ${theme.fg("dim", result.ledgerRunId ?? "")} ${theme.fg("muted", viewHint)}`);
|
|
812
832
|
if (expanded && result.output.trim()) lines.push(` ${theme.fg("muted", result.output.split("\n")[0] ?? "")}`);
|
|
813
833
|
}
|
|
814
834
|
if (hasMore) lines.push(theme.fg("dim", `└─ … ${results.length - maxRows} more result(s)`));
|
|
@@ -32,6 +32,7 @@ import {
|
|
|
32
32
|
delegationSignalColor,
|
|
33
33
|
extractDelegationSignalBadge,
|
|
34
34
|
finishDelegationRun,
|
|
35
|
+
formatDelegationCwdLabel,
|
|
35
36
|
formatDelegationModelLabel,
|
|
36
37
|
formatDelegationSignalBadge,
|
|
37
38
|
formatDuration,
|
|
@@ -109,6 +110,8 @@ export function registerDelegationTools(pi: ExtensionAPI, state: HarnessRuntimeS
|
|
|
109
110
|
"If agent routing is uncertain, call zob_delegation_catalog before the first delegation.",
|
|
110
111
|
"Use delegate_agent for broad discovery, external research, skeptical review, or independent QA before making risky edits.",
|
|
111
112
|
"When using delegate_agent, give each child a bounded six-part contract and a concrete final output shape.",
|
|
113
|
+
"Optional cwd spawns the child inside that repo-local working directory; tasks[].cwd and chain[].cwd override delegate_agent top-level cwd defaults.",
|
|
114
|
+
"cwd only selects the child working directory; it does not replace allowed_paths or write-scope grants.",
|
|
112
115
|
"If effective tools include edit/write, provide non-empty repo-relative-only allowed_paths; use repo-local reports/... snapshot/context_ref refs for external context.",
|
|
113
116
|
],
|
|
114
117
|
parameters: DelegateParams,
|
|
@@ -169,6 +172,7 @@ export function registerDelegationTools(pi: ExtensionAPI, state: HarnessRuntimeS
|
|
|
169
172
|
const startedAt = new Date(startedAtMs).toISOString();
|
|
170
173
|
const requestedTools = parseToolList(params.tools);
|
|
171
174
|
const effectiveThinking = item.thinking ?? params.thinking;
|
|
175
|
+
const cwdResult = resolveChildCwd(ctx.cwd, item.cwd ?? params.cwd);
|
|
172
176
|
startDelegationRun(state.delegations, {
|
|
173
177
|
id: runId,
|
|
174
178
|
parentToolCallId: toolCallId,
|
|
@@ -178,6 +182,7 @@ export function registerDelegationTools(pi: ExtensionAPI, state: HarnessRuntimeS
|
|
|
178
182
|
agent: item.agent,
|
|
179
183
|
task: taskText,
|
|
180
184
|
startedAtMs,
|
|
185
|
+
cwd: cwdResult.cwd,
|
|
181
186
|
});
|
|
182
187
|
renderDelegationMonitor();
|
|
183
188
|
const agent = byName.get(item.agent.toLowerCase());
|
|
@@ -193,6 +198,7 @@ export function registerDelegationTools(pi: ExtensionAPI, state: HarnessRuntimeS
|
|
|
193
198
|
gateErrors: ["unknown agent"],
|
|
194
199
|
failureKind: "config",
|
|
195
200
|
usage: usageEmpty(),
|
|
201
|
+
cwd: cwdResult.cwd,
|
|
196
202
|
};
|
|
197
203
|
const endedAtMs = Date.now();
|
|
198
204
|
const endedAt = new Date(endedAtMs).toISOString();
|
|
@@ -203,7 +209,7 @@ export function registerDelegationTools(pi: ExtensionAPI, state: HarnessRuntimeS
|
|
|
203
209
|
...delegationLedgerMeta("delegate_agent", toolCallId, monitor.mode, monitor.index),
|
|
204
210
|
agent: item.agent,
|
|
205
211
|
taskHash: sha256(taskText),
|
|
206
|
-
cwd:
|
|
212
|
+
cwd: cwdResult.cwd,
|
|
207
213
|
tools: requestedTools ?? [],
|
|
208
214
|
errors: ["unknown agent"],
|
|
209
215
|
failureKind: result.failureKind,
|
|
@@ -216,7 +222,7 @@ export function registerDelegationTools(pi: ExtensionAPI, state: HarnessRuntimeS
|
|
|
216
222
|
mode: state.activeMode,
|
|
217
223
|
agent: item.agent,
|
|
218
224
|
model: params.model,
|
|
219
|
-
cwd:
|
|
225
|
+
cwd: cwdResult.cwd,
|
|
220
226
|
tools: requestedTools ?? [],
|
|
221
227
|
taskHash: sha256(taskText),
|
|
222
228
|
outputContract: inferOutputContract(item.agent),
|
|
@@ -240,14 +246,14 @@ export function registerDelegationTools(pi: ExtensionAPI, state: HarnessRuntimeS
|
|
|
240
246
|
failureKind: result.failureKind,
|
|
241
247
|
errorMessage: "Configuration blocked; no child launched: unknown agent",
|
|
242
248
|
model: params.model,
|
|
249
|
+
cwd: cwdResult.cwd,
|
|
243
250
|
});
|
|
244
251
|
recordTodoClaimFromChildResult(pi, state, effectiveChildGoal, result);
|
|
245
252
|
renderDelegationMonitor();
|
|
246
253
|
return result;
|
|
247
254
|
}
|
|
248
255
|
|
|
249
|
-
updateDelegationRun(state.delegations, runId, { agent: agent.name });
|
|
250
|
-
const cwdResult = resolveChildCwd(ctx.cwd, item.cwd);
|
|
256
|
+
updateDelegationRun(state.delegations, runId, { agent: agent.name, cwd: cwdResult.cwd });
|
|
251
257
|
const effectiveTools = requestedTools ?? agent.tools ?? [];
|
|
252
258
|
const preflightErrors = [
|
|
253
259
|
...strictGoalErrors(state),
|
|
@@ -275,6 +281,7 @@ export function registerDelegationTools(pi: ExtensionAPI, state: HarnessRuntimeS
|
|
|
275
281
|
gateErrors: preflightErrors,
|
|
276
282
|
failureKind: classifyConfigOrPreflight(preflightErrors),
|
|
277
283
|
usage: usageEmpty(),
|
|
284
|
+
cwd: cwdResult.cwd,
|
|
278
285
|
};
|
|
279
286
|
const endedAtMs = Date.now();
|
|
280
287
|
const endedAt = new Date(endedAtMs).toISOString();
|
|
@@ -321,6 +328,7 @@ export function registerDelegationTools(pi: ExtensionAPI, state: HarnessRuntimeS
|
|
|
321
328
|
failureKind: result.failureKind,
|
|
322
329
|
errorMessage: `${result.failureKind === "config" ? "Configuration blocked" : "Preflight blocked"}; no child launched: ${preflightErrors.join("; ")}`,
|
|
323
330
|
model: params.model ?? agent.model,
|
|
331
|
+
cwd: cwdResult.cwd,
|
|
324
332
|
});
|
|
325
333
|
recordTodoClaimFromChildResult(pi, state, effectiveChildGoal, result);
|
|
326
334
|
renderDelegationMonitor();
|
|
@@ -349,6 +357,7 @@ export function registerDelegationTools(pi: ExtensionAPI, state: HarnessRuntimeS
|
|
|
349
357
|
const childPathPolicy = { allowedPaths: params.allowed_paths, forbiddenPaths: params.forbidden_paths };
|
|
350
358
|
const beforeChildDirty = toolsEnableWrites(effectiveChildTools) ? captureZcommitChildDirtySnapshot(ctx.cwd, childPathPolicy) : undefined;
|
|
351
359
|
const result = await runChildAgent(ctx, agent, taskText, cwdResult.cwd, signal, params.model, requestedTools?.join(","), (partial) => {
|
|
360
|
+
partial.cwd = cwdResult.cwd;
|
|
352
361
|
updateDelegationRun(state.delegations, runId, {
|
|
353
362
|
status: partial.stopReason === "aborted" ? "aborted" : "running",
|
|
354
363
|
agent: partial.agent,
|
|
@@ -356,6 +365,7 @@ export function registerDelegationTools(pi: ExtensionAPI, state: HarnessRuntimeS
|
|
|
356
365
|
outputPreview: partial.output,
|
|
357
366
|
stderrPreview: partial.stderr,
|
|
358
367
|
sessionPath: partial.sessionPath,
|
|
368
|
+
cwd: cwdResult.cwd,
|
|
359
369
|
stopReason: partial.stopReason,
|
|
360
370
|
errorMessage: partial.errorMessage,
|
|
361
371
|
usage: partial.usage,
|
|
@@ -363,6 +373,7 @@ export function registerDelegationTools(pi: ExtensionAPI, state: HarnessRuntimeS
|
|
|
363
373
|
renderDelegationMonitor();
|
|
364
374
|
update?.(partial);
|
|
365
375
|
}, childPathPolicy, effectiveThinking);
|
|
376
|
+
result.cwd = cwdResult.cwd;
|
|
366
377
|
result.childChangedPaths = captureChildDirtyDelta(ctx.cwd, childPathPolicy, beforeChildDirty);
|
|
367
378
|
result.ledgerRunId = runId;
|
|
368
379
|
result.outputContract = outputContract;
|
|
@@ -480,6 +491,7 @@ export function registerDelegationTools(pi: ExtensionAPI, state: HarnessRuntimeS
|
|
|
480
491
|
childChangedPaths: result.childChangedPaths,
|
|
481
492
|
usage: result.usage,
|
|
482
493
|
model: result.model,
|
|
494
|
+
cwd: cwdResult.cwd,
|
|
483
495
|
});
|
|
484
496
|
if (shouldRunAgenticClaimValidation(effectiveChildGoal, claimRecord)) {
|
|
485
497
|
await runAgenticTodoClaimValidation({ ctx, pi, state, childGoal: effectiveChildGoal, claimRecord, parentRunId: runId, appendDelegationLedger, signal, modelOverride: params.model, allowedPaths: params.allowed_paths, forbiddenPaths: params.forbidden_paths });
|
|
@@ -491,7 +503,7 @@ export function registerDelegationTools(pi: ExtensionAPI, state: HarnessRuntimeS
|
|
|
491
503
|
if (params.agent && params.task) {
|
|
492
504
|
startMonitorTicker();
|
|
493
505
|
try {
|
|
494
|
-
const result = await runOne({ agent: params.agent, task: params.task, thinking: params.thinking, child_goal: params.child_goal }, { mode: "single", index: 0 }, (partial) => {
|
|
506
|
+
const result = await runOne({ agent: params.agent, task: params.task, cwd: params.cwd, thinking: params.thinking, child_goal: params.child_goal }, { mode: "single", index: 0 }, (partial) => {
|
|
495
507
|
onUpdate?.({ content: [{ type: "text", text: partial.output || partial.stderr || "running..." }], details: makeDetails("single", [partial]) });
|
|
496
508
|
});
|
|
497
509
|
return {
|
|
@@ -519,7 +531,7 @@ export function registerDelegationTools(pi: ExtensionAPI, state: HarnessRuntimeS
|
|
|
519
531
|
return result;
|
|
520
532
|
});
|
|
521
533
|
const successCount = results.filter((result) => !isFailed(result)).length;
|
|
522
|
-
const summaries = results.map((result) => `### ${result.agent} — ${isFailed(result) ? "FAILED/INCOMPLETE" : "OK"}\n\n${capOutput(formatChildResultText(result))}`);
|
|
534
|
+
const summaries = results.map((result) => `### ${result.agent} — ${isFailed(result) ? "FAILED/INCOMPLETE" : "OK"}${formatDelegationCwdLabel(result, ctx.cwd) ? ` · ${formatDelegationCwdLabel(result, ctx.cwd)}` : ""}\n\n${capOutput(formatChildResultText(result))}`);
|
|
523
535
|
return {
|
|
524
536
|
content: [{ type: "text", text: `Parallel delegation: ${successCount}/${results.length} succeeded\n\n${summaries.join("\n\n---\n\n")}` }],
|
|
525
537
|
details: makeDetails("parallel", results),
|
|
@@ -542,7 +554,7 @@ export function registerDelegationTools(pi: ExtensionAPI, state: HarnessRuntimeS
|
|
|
542
554
|
results.push(result);
|
|
543
555
|
if (isFailed(result)) {
|
|
544
556
|
return {
|
|
545
|
-
content: [{ type: "text", text: `Chain stopped at step ${index + 1} (${result.agent}):\n\n${formatChildResultText(result)}` }],
|
|
557
|
+
content: [{ type: "text", text: `Chain stopped at step ${index + 1} (${result.agent}${formatDelegationCwdLabel(result, ctx.cwd) ? ` · ${formatDelegationCwdLabel(result, ctx.cwd)}` : ""}):\n\n${formatChildResultText(result)}` }],
|
|
546
558
|
details: makeDetails("chain", results),
|
|
547
559
|
};
|
|
548
560
|
}
|
|
@@ -572,6 +584,7 @@ export function registerDelegationTools(pi: ExtensionAPI, state: HarnessRuntimeS
|
|
|
572
584
|
"Use delegate_task when you need strict preflight rather than a freeform delegated prompt.",
|
|
573
585
|
"Normally omit output_contract; the harness infers it from the selected agent. Do not invent output contract ids.",
|
|
574
586
|
"Normally omit required_tools; the harness infers the selected agent's declared tools. Only set required_tools to intentionally narrow tools.",
|
|
587
|
+
"Optional cwd spawns the child inside that repo-local working directory; it only selects cwd and does not replace allowed_paths or write-scope grants.",
|
|
575
588
|
"If effective tools include edit/write, set top-level original_user_ask to the original human request; context or task text does not satisfy the strict write preflight gate.",
|
|
576
589
|
"Use canonical JSON keys expected_outcome, must_do, must_not_do, context, original_user_ask, allowed_paths, forbidden_paths; safe aliases are accepted only when non-conflicting.",
|
|
577
590
|
"Accepted aliases: expectedOutcome, mustDo, mustNotDo/must_not/mustNot, originalUserAsk, allowedPaths, forbiddenPaths, requiredTools, outputContract, runInBackground, childGoal, loadSkills.",
|
|
@@ -598,6 +611,7 @@ export function registerDelegationTools(pi: ExtensionAPI, state: HarnessRuntimeS
|
|
|
598
611
|
const effectiveChildGoal = childGoalResolution.childGoal;
|
|
599
612
|
const startedAtMs = Date.now();
|
|
600
613
|
const startedAt = new Date(startedAtMs).toISOString();
|
|
614
|
+
const cwdResult = resolveChildCwd(ctx.cwd, params.cwd);
|
|
601
615
|
const appendDelegationLedger = (entry: Record<string, unknown>): void => {
|
|
602
616
|
appendLedgerFile(ctx.cwd, entry);
|
|
603
617
|
pi.appendEntry("zob-delegation", entry);
|
|
@@ -635,6 +649,7 @@ export function registerDelegationTools(pi: ExtensionAPI, state: HarnessRuntimeS
|
|
|
635
649
|
agent: params.agent,
|
|
636
650
|
task: params.task,
|
|
637
651
|
startedAtMs,
|
|
652
|
+
cwd: cwdResult.cwd,
|
|
638
653
|
});
|
|
639
654
|
renderDelegationMonitor();
|
|
640
655
|
startMonitorTicker();
|
|
@@ -652,6 +667,7 @@ export function registerDelegationTools(pi: ExtensionAPI, state: HarnessRuntimeS
|
|
|
652
667
|
gateErrors: ["unknown agent"],
|
|
653
668
|
failureKind: "config",
|
|
654
669
|
usage: usageEmpty(),
|
|
670
|
+
cwd: cwdResult.cwd,
|
|
655
671
|
};
|
|
656
672
|
const endedAtMs = Date.now();
|
|
657
673
|
const endedAt = new Date(endedAtMs).toISOString();
|
|
@@ -662,7 +678,7 @@ export function registerDelegationTools(pi: ExtensionAPI, state: HarnessRuntimeS
|
|
|
662
678
|
...delegationLedgerMeta("delegate_task", toolCallId, "single", 0),
|
|
663
679
|
agent: params.agent,
|
|
664
680
|
taskHash: sha256(params.task),
|
|
665
|
-
cwd:
|
|
681
|
+
cwd: cwdResult.cwd,
|
|
666
682
|
tools: params.required_tools ?? [],
|
|
667
683
|
errors: ["unknown agent"],
|
|
668
684
|
failureKind: result.failureKind,
|
|
@@ -675,7 +691,7 @@ export function registerDelegationTools(pi: ExtensionAPI, state: HarnessRuntimeS
|
|
|
675
691
|
mode: state.activeMode,
|
|
676
692
|
agent: params.agent,
|
|
677
693
|
model: params.model,
|
|
678
|
-
cwd:
|
|
694
|
+
cwd: cwdResult.cwd,
|
|
679
695
|
tools: params.required_tools ?? [],
|
|
680
696
|
taskHash: sha256(params.task),
|
|
681
697
|
outputContract: params.output_contract ?? inferOutputContract(params.agent),
|
|
@@ -699,13 +715,14 @@ export function registerDelegationTools(pi: ExtensionAPI, state: HarnessRuntimeS
|
|
|
699
715
|
failureKind: result.failureKind,
|
|
700
716
|
errorMessage: "Configuration blocked; no child launched: unknown agent",
|
|
701
717
|
model: params.model,
|
|
718
|
+
cwd: cwdResult.cwd,
|
|
702
719
|
});
|
|
703
720
|
recordTodoClaimFromChildResult(pi, state, effectiveChildGoal, result);
|
|
704
721
|
stopMonitorTicker();
|
|
705
722
|
return { content: [{ type: "text", text: formatChildResultText(result) }], details: { mode: "single", results: [result], agents: agents.map((candidate) => candidate.name) } };
|
|
706
723
|
}
|
|
707
724
|
|
|
708
|
-
updateDelegationRun(state.delegations, runId, { agent: agent.name });
|
|
725
|
+
updateDelegationRun(state.delegations, runId, { agent: agent.name, cwd: cwdResult.cwd });
|
|
709
726
|
const requestedOutputContract = params.output_contract ?? inferOutputContract(agent.name);
|
|
710
727
|
const effectiveTools = params.required_tools?.length ? params.required_tools : agent.tools ?? [];
|
|
711
728
|
|
|
@@ -728,7 +745,6 @@ export function registerDelegationTools(pi: ExtensionAPI, state: HarnessRuntimeS
|
|
|
728
745
|
.filter((part): part is string => typeof part === "string")
|
|
729
746
|
.join("\n");
|
|
730
747
|
|
|
731
|
-
const cwdResult = resolveChildCwd(ctx.cwd, params.cwd);
|
|
732
748
|
const preflightErrors = [
|
|
733
749
|
...normalized.errors,
|
|
734
750
|
...strictGoalErrors(state),
|
|
@@ -759,6 +775,7 @@ export function registerDelegationTools(pi: ExtensionAPI, state: HarnessRuntimeS
|
|
|
759
775
|
gateErrors: preflightErrors,
|
|
760
776
|
failureKind: classifyConfigOrPreflight(preflightErrors),
|
|
761
777
|
usage: usageEmpty(),
|
|
778
|
+
cwd: cwdResult.cwd,
|
|
762
779
|
};
|
|
763
780
|
const endedAtMs = Date.now();
|
|
764
781
|
const endedAt = new Date(endedAtMs).toISOString();
|
|
@@ -805,6 +822,7 @@ export function registerDelegationTools(pi: ExtensionAPI, state: HarnessRuntimeS
|
|
|
805
822
|
failureKind: result.failureKind,
|
|
806
823
|
errorMessage: delegateTaskPreflightHelp(preflightErrors).replace(/\n/g, " "),
|
|
807
824
|
model: params.model ?? agent.model,
|
|
825
|
+
cwd: cwdResult.cwd,
|
|
808
826
|
});
|
|
809
827
|
recordTodoClaimFromChildResult(pi, state, effectiveChildGoal, result);
|
|
810
828
|
stopMonitorTicker();
|
|
@@ -834,6 +852,7 @@ export function registerDelegationTools(pi: ExtensionAPI, state: HarnessRuntimeS
|
|
|
834
852
|
const childPathPolicy = { allowedPaths: params.allowed_paths, forbiddenPaths: params.forbidden_paths };
|
|
835
853
|
const beforeChildDirty = toolsEnableWrites(effectiveTools) ? captureZcommitChildDirtySnapshot(ctx.cwd, childPathPolicy) : undefined;
|
|
836
854
|
const result = await runChildAgent(ctx, agent, structuredTask, cwdResult.cwd, childSignal, params.model, effectiveTools.join(","), (partial) => {
|
|
855
|
+
partial.cwd = cwdResult.cwd;
|
|
837
856
|
updateDelegationRun(state.delegations, runId, {
|
|
838
857
|
status: partial.stopReason === "aborted" ? "aborted" : "running",
|
|
839
858
|
agent: partial.agent,
|
|
@@ -841,6 +860,7 @@ export function registerDelegationTools(pi: ExtensionAPI, state: HarnessRuntimeS
|
|
|
841
860
|
outputPreview: partial.output,
|
|
842
861
|
stderrPreview: partial.stderr,
|
|
843
862
|
sessionPath: partial.sessionPath,
|
|
863
|
+
cwd: cwdResult.cwd,
|
|
844
864
|
stopReason: partial.stopReason,
|
|
845
865
|
errorMessage: partial.errorMessage,
|
|
846
866
|
usage: partial.usage,
|
|
@@ -848,6 +868,7 @@ export function registerDelegationTools(pi: ExtensionAPI, state: HarnessRuntimeS
|
|
|
848
868
|
renderDelegationMonitor();
|
|
849
869
|
if (emitToolUpdates) onUpdate?.({ content: [{ type: "text", text: partial.output || partial.stderr || "running..." }], details: { mode: "single", results: [partial], agents: agents.map((candidate) => candidate.name) } });
|
|
850
870
|
}, childPathPolicy, params.thinking);
|
|
871
|
+
result.cwd = cwdResult.cwd;
|
|
851
872
|
result.childChangedPaths = captureChildDirtyDelta(ctx.cwd, childPathPolicy, beforeChildDirty);
|
|
852
873
|
result.ledgerRunId = runId;
|
|
853
874
|
result.outputContract = outputContract;
|
|
@@ -966,6 +987,7 @@ export function registerDelegationTools(pi: ExtensionAPI, state: HarnessRuntimeS
|
|
|
966
987
|
childChangedPaths: result.childChangedPaths,
|
|
967
988
|
usage: result.usage,
|
|
968
989
|
model: result.model,
|
|
990
|
+
cwd: cwdResult.cwd,
|
|
969
991
|
});
|
|
970
992
|
if (shouldRunAgenticClaimValidation(effectiveChildGoal, claimRecord)) {
|
|
971
993
|
await runAgenticTodoClaimValidation({ ctx, pi, state, childGoal: effectiveChildGoal, claimRecord, parentRunId: runId, appendDelegationLedger, signal: childSignal, modelOverride: params.model, allowedPaths: params.allowed_paths, forbiddenPaths: params.forbidden_paths });
|
|
@@ -1092,6 +1114,7 @@ ${formatChildResultText(result)}` : result.output || "(no output)" }],
|
|
|
1092
1114
|
failureKind: result.failureKind,
|
|
1093
1115
|
stopReason: result.stopReason,
|
|
1094
1116
|
stopCondition: result.stopCondition,
|
|
1117
|
+
cwd: result.cwd,
|
|
1095
1118
|
sessionPath: result.sessionPath,
|
|
1096
1119
|
});
|
|
1097
1120
|
const resultDetails = (result: ChildResult) => includeResult ? { result } : { resultSummary: compactResult(result), resultIncluded: false };
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "zob-harness",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.12.0",
|
|
4
4
|
"type": "module",
|
|
5
5
|
"description": "A governed Agent Factory for Pi: launch communicating agent teams, run tmux-backed factories, validate artifacts, and package repeatable workflows.",
|
|
6
6
|
"license": "MIT",
|