ultimate-pi 0.9.1 → 0.10.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/.agents/skills/harness-decisions/SKILL.md +17 -13
- package/.agents/skills/harness-plan/SKILL.md +3 -3
- package/.pi/agents/harness/planner.md +8 -4
- package/.pi/extensions/harness-plan-approval.ts +140 -0
- package/.pi/extensions/harness-run-context.ts +29 -8
- package/.pi/extensions/lib/harness-subagents/harness-subagent-policy.ts +11 -1
- package/.pi/extensions/lib/harness-subagents/parent-ask-user-bridge.ts +8 -87
- package/.pi/extensions/lib/harness-subagents/parent-harness-ui-bridge.ts +306 -0
- package/.pi/extensions/lib/harness-subagents/parent-harness-ui-hooks.ts +59 -0
- package/.pi/extensions/lib/harness-subagents/spawn-policy.ts +9 -0
- package/.pi/extensions/lib/harness-subagents/vendored/agent-manager.ts +4 -0
- package/.pi/extensions/lib/harness-subagents/vendored/agent-runner.ts +39 -12
- package/.pi/extensions/lib/harness-subagents/vendored/index.ts +35 -11
- package/.pi/extensions/lib/plan-approval/create-plan.ts +131 -0
- package/.pi/extensions/lib/plan-approval/dialog.ts +207 -0
- package/.pi/extensions/lib/plan-approval/fallback.ts +50 -0
- package/.pi/extensions/lib/plan-approval/format-plan.ts +94 -0
- package/.pi/extensions/lib/plan-approval/render.ts +83 -0
- package/.pi/extensions/lib/plan-approval/schema.ts +39 -0
- package/.pi/extensions/lib/plan-approval/types.ts +32 -0
- package/.pi/extensions/lib/plan-approval/validate.ts +61 -0
- package/.pi/lib/harness-run-context.ts +117 -28
- package/.pi/prompts/harness-plan.md +6 -6
- package/CHANGELOG.md +6 -0
- package/package.json +3 -3
|
@@ -36,18 +36,22 @@ description: Structured user decisions via ask_user for harness setup, planning,
|
|
|
36
36
|
|
|
37
37
|
## Example (plan — approval gate)
|
|
38
38
|
|
|
39
|
-
|
|
39
|
+
`harness/planner` calls **`approve_plan`** with the full `plan_packet` (parent TUI: scrollable plan + Approve / Request changes / Cancel), then **`create_plan`** with the same packet after Approve. Do not use `ask_user` for final approval or `write`/`edit` for the plan file.
|
|
40
40
|
|
|
41
41
|
```json
|
|
42
42
|
{
|
|
43
|
-
"
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
43
|
+
"plan_packet": {
|
|
44
|
+
"schema_version": "1.0.0",
|
|
45
|
+
"contract_version": "1.0.0",
|
|
46
|
+
"plan_id": "…",
|
|
47
|
+
"task_id": "…",
|
|
48
|
+
"scope": "…",
|
|
49
|
+
"assumptions": [],
|
|
50
|
+
"risk_level": "med",
|
|
51
|
+
"acceptance_checks": ["…"],
|
|
52
|
+
"rollback_plan": { "revert_commit_ready": true, "rollback_artifacts": { "revert_command": "…", "revert_branch": "…", "patch_bundle": "…" } }
|
|
53
|
+
},
|
|
54
|
+
"human_summary": "One-line summary for the overlay header"
|
|
51
55
|
}
|
|
52
56
|
```
|
|
53
57
|
|
|
@@ -64,8 +68,8 @@ After presenting the full PlanPacket in chat:
|
|
|
64
68
|
}
|
|
65
69
|
```
|
|
66
70
|
|
|
67
|
-
## Who
|
|
71
|
+
## Who calls what
|
|
68
72
|
|
|
69
|
-
- `harness/planner` —
|
|
70
|
-
- `harness/evaluator`, `harness/adversary`, and `harness/tie-breaker` — emit `human_required
|
|
71
|
-
-
|
|
73
|
+
- `harness/planner` — `ask_user` for clarification; **`approve_plan`** then **`create_plan`** for the plan file (`write`/`edit` blocked).
|
|
74
|
+
- `harness/evaluator`, `harness/adversary`, and `harness/tie-breaker` — emit `human_required`; the **parent orchestrator** calls `ask_user`.
|
|
75
|
+
- Parent orchestrator during `/harness-plan` — must **not** call `ask_user`, `approve_plan`, or `create_plan` (planner owns the full plan lifecycle).
|
|
@@ -17,12 +17,12 @@ description: Produce PlanPacket-aligned harness plans before execute phase. Use
|
|
|
17
17
|
1. Use `HarnessSpawnContext` from injected `[HarnessRunContext]` — do not read spec files from disk.
|
|
18
18
|
2. Spawn `harness/planner` **once** with that JSON in the prompt (`inherit_context: false`).
|
|
19
19
|
3. Parse planner JSON from `get_subagent_result` (`status`, `plan_packet`, `clarification`).
|
|
20
|
-
4. Do **not** parent `ask_user` or re-spawn
|
|
21
|
-
5.
|
|
20
|
+
4. Do **not** parent `ask_user` / `approve_plan` / `create_plan` or re-spawn — planner uses those tools in the subagent (bridged UI + `create_plan` write).
|
|
21
|
+
5. Parent checks `plan_ready` on `harness-run-context` after planner returns — **does not** write `plan-packet.json`.
|
|
22
22
|
|
|
23
23
|
## Rules
|
|
24
24
|
|
|
25
|
-
- `harness/planner` owns clarification
|
|
25
|
+
- `harness/planner` owns clarification (`ask_user`), approval (`approve_plan`), and persistence (`create_plan` — only path to `plan-packet.json`; `write`/`edit` blocked).
|
|
26
26
|
- Never plan or mutate source inline in the slash-command session.
|
|
27
27
|
- context-mode only on harness paths; never lean-ctx.
|
|
28
28
|
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
description: Harness planner that compiles strict PlanPacket contracts before execution.
|
|
3
|
-
tools: read, grep, find, ls, ask_user
|
|
3
|
+
tools: read, grep, find, ls, ask_user, approve_plan, create_plan
|
|
4
|
+
disallowed_tools: write, edit, bash
|
|
4
5
|
extensions: false
|
|
5
6
|
thinking: medium
|
|
6
7
|
max_turns: 20
|
|
@@ -11,7 +12,7 @@ You are the Harness Planner.
|
|
|
11
12
|
|
|
12
13
|
## Mission
|
|
13
14
|
|
|
14
|
-
Compile a strict, machine-readable `PlanPacket
|
|
15
|
+
Compile a strict, machine-readable `PlanPacket`, get user approval, and persist it with **`create_plan`**. You do **not** use `write` or `edit` — those are blocked. The parent orchestrator does not write `plan-packet.json`.
|
|
15
16
|
|
|
16
17
|
## Spawn context
|
|
17
18
|
|
|
@@ -26,11 +27,12 @@ Read the `HarnessSpawnContext` JSON in the spawn prompt (`schema_version`, `mode
|
|
|
26
27
|
5. Build a complete `PlanPacket`: `plan_id`, `task_id`, `scope`, `assumptions`, `risk_level`, `acceptance_checks`, `rollback_plan` with `revert_command`, `revert_branch`, `patch_bundle`, `revert_commit_ready: true`.
|
|
27
28
|
6. Escalate `risk_level` to `high` for blast radius, uncertainty, or policy-sensitive surfaces.
|
|
28
29
|
7. If scope is ambiguous, call `ask_user` with structured options — do not return `needs_clarification` without trying `ask_user` first when options are clear.
|
|
29
|
-
8.
|
|
30
|
+
8. Call **`approve_plan`** with the full `plan_packet` (and optional `human_summary`). The parent TUI shows a scrollable plan plus **Approve** / **Request changes** / **Cancel**. On Request changes, revise and call `approve_plan` again.
|
|
31
|
+
9. After the user selects **Approve**, call **`create_plan`** with the same `plan_packet` to write canonical `plan-packet.json` for this run.
|
|
30
32
|
|
|
31
33
|
## Guardrails
|
|
32
34
|
|
|
33
|
-
-
|
|
35
|
+
- Never call `write`, `edit`, or mutating `bash` — use **`create_plan`** only for the plan file.
|
|
34
36
|
- Never speculate about code you have not read.
|
|
35
37
|
- Do not execute or widen implementation scope.
|
|
36
38
|
|
|
@@ -48,3 +50,5 @@ End with a single fenced `json` block the parent can parse:
|
|
|
48
50
|
```
|
|
49
51
|
|
|
50
52
|
Use `"status": "needs_clarification"` only when blocked after `ask_user` or user cancelled; include `clarification` when the parent must intervene without a live subagent.
|
|
53
|
+
|
|
54
|
+
When `create_plan` succeeds, set `status` to `"ready"` and confirm `plan_packet_path` was written.
|
|
@@ -0,0 +1,140 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* harness-plan-approval — PlanPacket approval UI and transcript renderer for parent sessions.
|
|
3
|
+
*/
|
|
4
|
+
|
|
5
|
+
import type { ExtensionAPI } from "@earendil-works/pi-coding-agent";
|
|
6
|
+
import { Text } from "@earendil-works/pi-tui";
|
|
7
|
+
import {
|
|
8
|
+
appendPlanApprovalIfNew,
|
|
9
|
+
getLatestRunContext,
|
|
10
|
+
parsePlanApprovalFromMessage,
|
|
11
|
+
} from "../lib/harness-run-context.js";
|
|
12
|
+
import { runPlanApprovalDialog } from "./lib/plan-approval/dialog.js";
|
|
13
|
+
import { runPlanApprovalFallback } from "./lib/plan-approval/fallback.js";
|
|
14
|
+
import {
|
|
15
|
+
renderApprovePlanCall,
|
|
16
|
+
renderApprovePlanResult,
|
|
17
|
+
renderHarnessPlanDraft,
|
|
18
|
+
} from "./lib/plan-approval/render.js";
|
|
19
|
+
import {
|
|
20
|
+
ApprovePlanParamsSchema,
|
|
21
|
+
PROMPT_GUIDELINES,
|
|
22
|
+
PROMPT_SNIPPET,
|
|
23
|
+
} from "./lib/plan-approval/schema.js";
|
|
24
|
+
import type {
|
|
25
|
+
ApprovePlanParams,
|
|
26
|
+
PlanApprovalDialogResult,
|
|
27
|
+
} from "./lib/plan-approval/types.js";
|
|
28
|
+
import {
|
|
29
|
+
formatApprovePlanResultText,
|
|
30
|
+
toApprovePlanToolDetails,
|
|
31
|
+
validateApprovePlanParams,
|
|
32
|
+
} from "./lib/plan-approval/validate.js";
|
|
33
|
+
|
|
34
|
+
export default function harnessPlanApproval(pi: ExtensionAPI) {
|
|
35
|
+
pi.registerMessageRenderer("harness-plan-draft", (message, _options, theme) => {
|
|
36
|
+
const data = message.details as
|
|
37
|
+
| {
|
|
38
|
+
plan_packet?: unknown;
|
|
39
|
+
human_summary?: string | null;
|
|
40
|
+
}
|
|
41
|
+
| undefined;
|
|
42
|
+
if (!data?.plan_packet) return undefined;
|
|
43
|
+
const lines = renderHarnessPlanDraft(
|
|
44
|
+
{
|
|
45
|
+
plan_packet: data.plan_packet as Parameters<
|
|
46
|
+
typeof renderHarnessPlanDraft
|
|
47
|
+
>[0]["plan_packet"],
|
|
48
|
+
human_summary: data.human_summary,
|
|
49
|
+
},
|
|
50
|
+
80,
|
|
51
|
+
theme,
|
|
52
|
+
);
|
|
53
|
+
return new Text(lines.join("\n"), 0, 0);
|
|
54
|
+
});
|
|
55
|
+
|
|
56
|
+
pi.registerTool({
|
|
57
|
+
name: "approve_plan",
|
|
58
|
+
label: "Approve Plan",
|
|
59
|
+
description:
|
|
60
|
+
"Present a PlanPacket for user approval with a scrollable plan view. Planners should prefer the subagent bridge; this registers the tool on parent sessions for non-interactive fallback.",
|
|
61
|
+
promptSnippet: PROMPT_SNIPPET,
|
|
62
|
+
promptGuidelines: PROMPT_GUIDELINES,
|
|
63
|
+
parameters: ApprovePlanParamsSchema,
|
|
64
|
+
|
|
65
|
+
async execute(_toolCallId, params, _signal, _onUpdate, ctx) {
|
|
66
|
+
const validated = validateApprovePlanParams(params as ApprovePlanParams);
|
|
67
|
+
if (typeof validated === "string") {
|
|
68
|
+
return {
|
|
69
|
+
content: [{ type: "text", text: validated }],
|
|
70
|
+
details: {
|
|
71
|
+
plan_packet: (params as ApprovePlanParams).plan_packet ?? {},
|
|
72
|
+
options: [],
|
|
73
|
+
response: null,
|
|
74
|
+
cancelled: true,
|
|
75
|
+
},
|
|
76
|
+
};
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
const planId = String(validated.plan_packet.plan_id ?? "plan");
|
|
80
|
+
const summary =
|
|
81
|
+
validated.human_summary?.trim() ||
|
|
82
|
+
`Plan ${planId} — pending your approval`;
|
|
83
|
+
pi.sendMessage({
|
|
84
|
+
customType: "harness-plan-draft",
|
|
85
|
+
content: summary,
|
|
86
|
+
display: true,
|
|
87
|
+
details: {
|
|
88
|
+
schema_version: "1.0.0",
|
|
89
|
+
plan_packet: validated.plan_packet,
|
|
90
|
+
human_summary: validated.human_summary ?? null,
|
|
91
|
+
shown_at: new Date().toISOString(),
|
|
92
|
+
},
|
|
93
|
+
});
|
|
94
|
+
|
|
95
|
+
let outcome: PlanApprovalDialogResult;
|
|
96
|
+
if (ctx.hasUI) {
|
|
97
|
+
outcome = await runPlanApprovalDialog(ctx.ui, validated);
|
|
98
|
+
} else {
|
|
99
|
+
outcome = await runPlanApprovalFallback(ctx.ui, validated);
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
const details = toApprovePlanToolDetails(
|
|
103
|
+
validated,
|
|
104
|
+
outcome.response,
|
|
105
|
+
outcome.cancelled,
|
|
106
|
+
);
|
|
107
|
+
const approval = parsePlanApprovalFromMessage({
|
|
108
|
+
toolName: "approve_plan",
|
|
109
|
+
details,
|
|
110
|
+
});
|
|
111
|
+
if (approval) {
|
|
112
|
+
const entries = ctx.sessionManager.getEntries();
|
|
113
|
+
const runCtx = getLatestRunContext(entries);
|
|
114
|
+
appendPlanApprovalIfNew(
|
|
115
|
+
(type, data) => pi.appendEntry(type, data),
|
|
116
|
+
entries,
|
|
117
|
+
approval,
|
|
118
|
+
runCtx,
|
|
119
|
+
);
|
|
120
|
+
}
|
|
121
|
+
|
|
122
|
+
const text = formatApprovePlanResultText(
|
|
123
|
+
outcome.response,
|
|
124
|
+
outcome.cancelled,
|
|
125
|
+
);
|
|
126
|
+
return {
|
|
127
|
+
content: [{ type: "text", text }],
|
|
128
|
+
details,
|
|
129
|
+
};
|
|
130
|
+
},
|
|
131
|
+
|
|
132
|
+
renderCall(args, theme) {
|
|
133
|
+
return renderApprovePlanCall(args, theme);
|
|
134
|
+
},
|
|
135
|
+
|
|
136
|
+
renderResult(result, options, theme) {
|
|
137
|
+
return renderApprovePlanResult(result, options, theme);
|
|
138
|
+
},
|
|
139
|
+
});
|
|
140
|
+
}
|
|
@@ -26,14 +26,15 @@ import {
|
|
|
26
26
|
isAmendPlanAllowed,
|
|
27
27
|
isHarnessBootstrapPrompt,
|
|
28
28
|
isNewTaskPlanBlocked,
|
|
29
|
+
isPlanApprovalAskUser,
|
|
29
30
|
isStaleActiveRunPointer,
|
|
30
31
|
loadProjectActiveRun,
|
|
31
32
|
loadRunContextFromDisk,
|
|
32
33
|
nextStepAfterOutcome,
|
|
33
34
|
nowIso,
|
|
34
35
|
type PlanPacketSummary,
|
|
35
|
-
parseAskUserApprovalFromMessage,
|
|
36
36
|
parseHarnessSlashInput,
|
|
37
|
+
parsePlanApprovalFromMessage,
|
|
37
38
|
planPacketSummary,
|
|
38
39
|
readPlanPacketFromPath,
|
|
39
40
|
resolveArgsForCommand,
|
|
@@ -582,7 +583,7 @@ export default function harnessRunContext(pi: ExtensionAPI) {
|
|
|
582
583
|
activeCtx.last_outcome = "needs_clarification";
|
|
583
584
|
activeCtx.last_completed_step = "plan";
|
|
584
585
|
const msg =
|
|
585
|
-
"Plan file exists but user approval was not recorded.
|
|
586
|
+
"Plan file exists but user approval was not recorded. Planner must call approve_plan (or bridged ask_user Approve) before writing plan-packet.json.";
|
|
586
587
|
if (ctx.hasUI) ctx.ui.notify(msg, "warning");
|
|
587
588
|
else
|
|
588
589
|
pi.sendMessage({
|
|
@@ -649,9 +650,12 @@ export default function harnessRunContext(pi: ExtensionAPI) {
|
|
|
649
650
|
});
|
|
650
651
|
|
|
651
652
|
pi.on("tool_result", async (event, ctx) => {
|
|
652
|
-
if (event.
|
|
653
|
-
|
|
654
|
-
|
|
653
|
+
if (event.isError) return;
|
|
654
|
+
if (event.toolName !== "ask_user" && event.toolName !== "approve_plan") {
|
|
655
|
+
return;
|
|
656
|
+
}
|
|
657
|
+
const approval = parsePlanApprovalFromMessage({
|
|
658
|
+
toolName: event.toolName,
|
|
655
659
|
details: event.details,
|
|
656
660
|
content: event.content,
|
|
657
661
|
});
|
|
@@ -662,11 +666,27 @@ export default function harnessRunContext(pi: ExtensionAPI) {
|
|
|
662
666
|
pi.appendEntry("harness-plan-approval", {
|
|
663
667
|
plan_id: approval.plan_id ?? runCtx.plan_id,
|
|
664
668
|
approved_at: approval.approved_at,
|
|
665
|
-
source:
|
|
669
|
+
source: approval.source,
|
|
666
670
|
});
|
|
667
671
|
});
|
|
668
672
|
|
|
669
|
-
pi.on("tool_call", async (event) => {
|
|
673
|
+
pi.on("tool_call", async (event, ctx) => {
|
|
674
|
+
if (event.toolName === "ask_user" && activeCtx?.plan_packet_path) {
|
|
675
|
+
const input = event.input as {
|
|
676
|
+
question?: string;
|
|
677
|
+
options?: unknown[];
|
|
678
|
+
};
|
|
679
|
+
if (
|
|
680
|
+
isPlanApprovalAskUser(input) &&
|
|
681
|
+
hasPlanUserApproval(getEntries(ctx), { sincePlanCommand: true })
|
|
682
|
+
) {
|
|
683
|
+
return {
|
|
684
|
+
block: true,
|
|
685
|
+
reason:
|
|
686
|
+
"harness-run-context: plan already approved via planner subagent; do not call ask_user for plan approval in the parent session.",
|
|
687
|
+
};
|
|
688
|
+
}
|
|
689
|
+
}
|
|
670
690
|
if (!activeCtx?.plan_packet_path) return undefined;
|
|
671
691
|
const phase = activeCtx.phase;
|
|
672
692
|
if (phase !== "evaluate" && phase !== "adversary") return undefined;
|
|
@@ -807,7 +827,8 @@ export default function harnessRunContext(pi: ExtensionAPI) {
|
|
|
807
827
|
}
|
|
808
828
|
const target = runCtx.plan_packet_path;
|
|
809
829
|
if (!target) {
|
|
810
|
-
if (ctx.hasUI)
|
|
830
|
+
if (ctx.hasUI)
|
|
831
|
+
ctx.ui.notify("No plan_packet_path on active run.", "error");
|
|
811
832
|
return;
|
|
812
833
|
}
|
|
813
834
|
if (pathArg && pathArg !== target) {
|
|
@@ -96,10 +96,20 @@ export function evaluateHarnessSubagentToolCall(
|
|
|
96
96
|
return { action: "allow" };
|
|
97
97
|
}
|
|
98
98
|
|
|
99
|
+
if (toolName === "create_plan") {
|
|
100
|
+
if (kind === "planner") {
|
|
101
|
+
return { action: "allow" };
|
|
102
|
+
}
|
|
103
|
+
return {
|
|
104
|
+
action: "block",
|
|
105
|
+
reason: `harness-subagent-policy: create_plan is only for harness/planner.`,
|
|
106
|
+
};
|
|
107
|
+
}
|
|
108
|
+
|
|
99
109
|
if (MUTATING_TOOLS.has(toolName)) {
|
|
100
110
|
return {
|
|
101
111
|
action: "block",
|
|
102
|
-
reason: `harness-subagent-policy: ${toolName} blocked for harness/${kind} (read-only phase agent).`,
|
|
112
|
+
reason: `harness-subagent-policy: ${toolName} blocked for harness/${kind} (read-only phase agent). Use create_plan after approve_plan instead of write/edit.`,
|
|
103
113
|
};
|
|
104
114
|
}
|
|
105
115
|
|
|
@@ -1,89 +1,10 @@
|
|
|
1
1
|
/**
|
|
2
|
-
*
|
|
2
|
+
* @deprecated Import from parent-harness-ui-bridge.js — kept for stable import paths.
|
|
3
3
|
*/
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
import { renderAskCall, renderAskResult } from "../ask-user/render.js";
|
|
12
|
-
import {
|
|
13
|
-
AskUserParamsSchema,
|
|
14
|
-
PROMPT_GUIDELINES,
|
|
15
|
-
PROMPT_SNIPPET,
|
|
16
|
-
} from "../ask-user/schema.js";
|
|
17
|
-
import type { AskUserParams, DialogResult } from "../ask-user/types.js";
|
|
18
|
-
import {
|
|
19
|
-
formatResultText,
|
|
20
|
-
toToolDetails,
|
|
21
|
-
validateAskParams,
|
|
22
|
-
} from "../ask-user/validate.js";
|
|
23
|
-
|
|
24
|
-
const ASK_USER_AGENT_TYPES = new Set([
|
|
25
|
-
"harness/planner",
|
|
26
|
-
"harness/evaluator",
|
|
27
|
-
"harness/adversary",
|
|
28
|
-
"harness/tie-breaker",
|
|
29
|
-
]);
|
|
30
|
-
|
|
31
|
-
export function agentTypeAllowsParentAskUser(agentType: string): boolean {
|
|
32
|
-
return ASK_USER_AGENT_TYPES.has(agentType);
|
|
33
|
-
}
|
|
34
|
-
|
|
35
|
-
export function createParentAskUserBridgeFactory(
|
|
36
|
-
parentCtx: ExtensionContext,
|
|
37
|
-
agentType: string,
|
|
38
|
-
): ((pi: ExtensionAPI) => void) | null {
|
|
39
|
-
if (!agentTypeAllowsParentAskUser(agentType)) {
|
|
40
|
-
return null;
|
|
41
|
-
}
|
|
42
|
-
return (pi: ExtensionAPI) => {
|
|
43
|
-
pi.registerTool({
|
|
44
|
-
name: "ask_user",
|
|
45
|
-
label: "Ask User",
|
|
46
|
-
description:
|
|
47
|
-
"Ask the user a structured question (parent session UI). Use for clarification and plan approval.",
|
|
48
|
-
promptSnippet: PROMPT_SNIPPET,
|
|
49
|
-
promptGuidelines: PROMPT_GUIDELINES,
|
|
50
|
-
parameters: AskUserParamsSchema,
|
|
51
|
-
async execute(_toolCallId, params, _signal, _onUpdate) {
|
|
52
|
-
const validated = validateAskParams(params as AskUserParams);
|
|
53
|
-
if (typeof validated === "string") {
|
|
54
|
-
return {
|
|
55
|
-
content: [{ type: "text", text: validated }],
|
|
56
|
-
details: {
|
|
57
|
-
question: params.question ?? "",
|
|
58
|
-
options: [],
|
|
59
|
-
response: null,
|
|
60
|
-
cancelled: true,
|
|
61
|
-
},
|
|
62
|
-
};
|
|
63
|
-
}
|
|
64
|
-
let outcome: DialogResult;
|
|
65
|
-
if (parentCtx.hasUI) {
|
|
66
|
-
outcome = await runAskDialog(parentCtx.ui, validated);
|
|
67
|
-
} else {
|
|
68
|
-
outcome = await runAskFallback(parentCtx.ui, validated);
|
|
69
|
-
}
|
|
70
|
-
const details = toToolDetails(
|
|
71
|
-
validated,
|
|
72
|
-
outcome.response,
|
|
73
|
-
outcome.cancelled,
|
|
74
|
-
);
|
|
75
|
-
const text = formatResultText(outcome.response, outcome.cancelled);
|
|
76
|
-
return {
|
|
77
|
-
content: [{ type: "text", text }],
|
|
78
|
-
details,
|
|
79
|
-
};
|
|
80
|
-
},
|
|
81
|
-
renderCall(args, theme) {
|
|
82
|
-
return renderAskCall(args, theme);
|
|
83
|
-
},
|
|
84
|
-
renderResult(result, options, theme) {
|
|
85
|
-
return renderAskResult(result, options, theme);
|
|
86
|
-
},
|
|
87
|
-
});
|
|
88
|
-
};
|
|
89
|
-
}
|
|
4
|
+
export {
|
|
5
|
+
agentTypeAllowsParentAskUser,
|
|
6
|
+
agentTypeAllowsParentHarnessUi,
|
|
7
|
+
createParentAskUserBridgeFactory,
|
|
8
|
+
createParentHarnessUiBridgeFactory,
|
|
9
|
+
type ParentHarnessUiHooks,
|
|
10
|
+
} from "./parent-harness-ui-bridge.js";
|