@howaboua/pi-ask 0.0.7 → 0.0.9
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 +8 -0
- package/ask/contracts.ts +9 -18
- package/ask/coordinator.ts +25 -14
- package/ask/tool.ts +17 -18
- package/index.ts +29 -9
- package/package.json +6 -6
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,13 @@
|
|
|
1
1
|
# @howaboua/pi-ask
|
|
2
2
|
|
|
3
|
+
## 0.0.9
|
|
4
|
+
|
|
5
|
+
- Removed redundant tool guidance from Ask, Shepherdr, Skills and Browser. Code and Notebook Mode now show one callable contract per tool, with detailed Browser and agent rules in help.
|
|
6
|
+
|
|
7
|
+
## 0.0.8
|
|
8
|
+
|
|
9
|
+
- Shepherdr now discovers and answers waiting questions invoked inside Code and Notebook Mode.
|
|
10
|
+
|
|
3
11
|
## 0.0.7
|
|
4
12
|
|
|
5
13
|
- The `ask` tool now supports steering questions that return immediately, preserve the full response panel, and deliver answers at the next safe boundary using the developer role under active Pi Codex Responses.
|
package/ask/contracts.ts
CHANGED
|
@@ -4,30 +4,21 @@ import { type Static, Type } from "typebox";
|
|
|
4
4
|
const ASK_DELIVERIES = ["wait", "steer"] as const;
|
|
5
5
|
|
|
6
6
|
const ChoiceSchema = Type.Object({
|
|
7
|
-
label: Type.String(
|
|
8
|
-
description: Type.Optional(Type.String(
|
|
7
|
+
label: Type.String(),
|
|
8
|
+
description: Type.Optional(Type.String()),
|
|
9
9
|
});
|
|
10
10
|
|
|
11
11
|
const PromptSchema = Type.Object({
|
|
12
|
-
title: Type.String(
|
|
13
|
-
body: Type.Optional(Type.String(
|
|
14
|
-
multiple: Type.Optional(Type.Boolean(
|
|
15
|
-
choices: Type.Optional(
|
|
16
|
-
Type.Array(ChoiceSchema, { description: "Choices; omit for free text." }),
|
|
17
|
-
),
|
|
12
|
+
title: Type.String(),
|
|
13
|
+
body: Type.Optional(Type.String()),
|
|
14
|
+
multiple: Type.Optional(Type.Boolean()),
|
|
15
|
+
choices: Type.Optional(Type.Array(ChoiceSchema)),
|
|
18
16
|
});
|
|
19
17
|
|
|
20
18
|
export const AskParameters = Type.Object({
|
|
21
|
-
handoff: Type.Optional(
|
|
22
|
-
|
|
23
|
-
),
|
|
24
|
-
prompts: Type.Array(PromptSchema, { description: "Prompts." }),
|
|
25
|
-
delivery: Type.Optional(
|
|
26
|
-
StringEnum(ASK_DELIVERIES, {
|
|
27
|
-
description:
|
|
28
|
-
"Wait for a gating response; steer while continuing reversible work. Omit to wait.",
|
|
29
|
-
}),
|
|
30
|
-
),
|
|
19
|
+
handoff: Type.Optional(Type.Boolean()),
|
|
20
|
+
prompts: Type.Array(PromptSchema),
|
|
21
|
+
delivery: Type.Optional(StringEnum(ASK_DELIVERIES)),
|
|
31
22
|
});
|
|
32
23
|
|
|
33
24
|
export type PromptChoice = Static<typeof ChoiceSchema>;
|
package/ask/coordinator.ts
CHANGED
|
@@ -17,6 +17,7 @@ export interface AskCoordinatorOptions {
|
|
|
17
17
|
}
|
|
18
18
|
|
|
19
19
|
interface PresentAskOptions {
|
|
20
|
+
onActiveChange?: (active: boolean) => void;
|
|
20
21
|
handoff?: boolean;
|
|
21
22
|
steering?: boolean;
|
|
22
23
|
signal?: AbortSignal;
|
|
@@ -42,23 +43,33 @@ export function createAskCoordinator({
|
|
|
42
43
|
const present = (
|
|
43
44
|
ctx: ExtensionContext,
|
|
44
45
|
prompts: AskPrompt[],
|
|
45
|
-
{
|
|
46
|
+
{
|
|
47
|
+
handoff = false,
|
|
48
|
+
steering = false,
|
|
49
|
+
signal,
|
|
50
|
+
onActiveChange,
|
|
51
|
+
}: PresentAskOptions = {},
|
|
46
52
|
): Promise<unknown> => {
|
|
47
53
|
const run = async () => {
|
|
48
54
|
if (signal?.aborted) return null;
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
? await
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
55
|
+
try {
|
|
56
|
+
onActiveChange?.(true);
|
|
57
|
+
return askInComposer
|
|
58
|
+
? await askInComposer(prompts, signal)
|
|
59
|
+
: ctx.mode === "tui"
|
|
60
|
+
? await askInTui(ctx, prompts, {
|
|
61
|
+
handoff,
|
|
62
|
+
steering,
|
|
63
|
+
...(signal ? { signal } : {}),
|
|
64
|
+
})
|
|
65
|
+
: await askWithPiUi(ctx, prompts, {
|
|
66
|
+
handoff,
|
|
67
|
+
steering,
|
|
68
|
+
...(signal ? { signal } : {}),
|
|
69
|
+
});
|
|
70
|
+
} finally {
|
|
71
|
+
onActiveChange?.(false);
|
|
72
|
+
}
|
|
62
73
|
};
|
|
63
74
|
const result = presentationTail.then(run, run);
|
|
64
75
|
presentationTail = result.then(
|
package/ask/tool.ts
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { defineTool } from "@earendil-works/pi-coding-agent";
|
|
2
2
|
import { Text } from "@earendil-works/pi-tui";
|
|
3
|
-
import { AskParameters } from "./contracts.js";
|
|
3
|
+
import { AskParameters, type AskPrompt } from "./contracts.js";
|
|
4
4
|
import {
|
|
5
5
|
type AskCoordinatorOptions,
|
|
6
6
|
createAskCoordinator,
|
|
@@ -18,6 +18,8 @@ interface BlockedState {
|
|
|
18
18
|
active: boolean;
|
|
19
19
|
label: string;
|
|
20
20
|
prompt: string;
|
|
21
|
+
handoff: boolean;
|
|
22
|
+
prompts: AskPrompt[];
|
|
21
23
|
}
|
|
22
24
|
|
|
23
25
|
type OnBlockedChange = (state: BlockedState) => void;
|
|
@@ -41,13 +43,12 @@ export function createAskRuntime({
|
|
|
41
43
|
const tool = defineTool({
|
|
42
44
|
name: "ask",
|
|
43
45
|
label: "Ask",
|
|
44
|
-
description: "Request user input or action
|
|
46
|
+
description: "Request user input or action",
|
|
45
47
|
parameters: AskParameters,
|
|
46
|
-
promptSnippet: "Request human input or action.",
|
|
47
48
|
promptGuidelines: [
|
|
48
|
-
"ask:
|
|
49
|
+
"ask: Omit delivery to wait; steer only while reversible work can continue. Handoffs wait for user action; state the completion signal",
|
|
49
50
|
"ask: For reviews, make each finding a prompt with disposition choices; do not report first.",
|
|
50
|
-
"ask:
|
|
51
|
+
"ask: Other/rephrase is automatic; omit choices for free text",
|
|
51
52
|
],
|
|
52
53
|
executionMode: "sequential",
|
|
53
54
|
async execute(toolCallId, params, signal, _onUpdate, ctx) {
|
|
@@ -79,24 +80,22 @@ export function createAskRuntime({
|
|
|
79
80
|
const blockedState = {
|
|
80
81
|
id: toolCallId,
|
|
81
82
|
active: true,
|
|
83
|
+
handoff,
|
|
84
|
+
prompts,
|
|
82
85
|
label: handoff ? "Human action needed" : "Waiting for input",
|
|
83
86
|
prompt: handoff
|
|
84
87
|
? "The user needs to complete an action before the active work can continue. Please announce this briefly in your natural voice."
|
|
85
88
|
: "User input is required before the active work can continue. Please announce this briefly in your natural voice.",
|
|
86
89
|
};
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
});
|
|
97
|
-
} finally {
|
|
98
|
-
onBlockedChange?.({ ...blockedState, active: false });
|
|
99
|
-
}
|
|
90
|
+
const presentationSignal = signal
|
|
91
|
+
? AbortSignal.any([signal, coordinator.sessionSignal])
|
|
92
|
+
: coordinator.sessionSignal;
|
|
93
|
+
const rawResponses = await coordinator.present(ctx, prompts, {
|
|
94
|
+
handoff,
|
|
95
|
+
signal: presentationSignal,
|
|
96
|
+
onActiveChange: (active) =>
|
|
97
|
+
onBlockedChange?.({ ...blockedState, active }),
|
|
98
|
+
});
|
|
100
99
|
const responses = normalizeResponses(prompts, rawResponses);
|
|
101
100
|
if (!responses) {
|
|
102
101
|
return {
|
package/index.ts
CHANGED
|
@@ -29,15 +29,35 @@ export default async function humanInTheLoop(pi: ExtensionAPI): Promise<void> {
|
|
|
29
29
|
const askRuntime = createAskRuntime({
|
|
30
30
|
deliverSteer: createSteerDelivery(pi, trySendCodexDeveloperMessage),
|
|
31
31
|
onBlockedChange: (state) => {
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
32
|
+
const notify = () => {
|
|
33
|
+
pi.events.emit(REALTIME_VOICE_PROMPT_CHANNEL, {
|
|
34
|
+
id: state.id,
|
|
35
|
+
active: state.active,
|
|
36
|
+
prompt: state.prompt,
|
|
37
|
+
});
|
|
38
|
+
pi.events.emit("herdr:blocked", {
|
|
39
|
+
active: state.active,
|
|
40
|
+
label: state.label,
|
|
41
|
+
});
|
|
42
|
+
};
|
|
43
|
+
// Publish before Herdr reports blocked, including calls nested inside exec.
|
|
44
|
+
try {
|
|
45
|
+
pi.appendEntry(
|
|
46
|
+
"pi-ask-active",
|
|
47
|
+
state.active
|
|
48
|
+
? {
|
|
49
|
+
version: 1,
|
|
50
|
+
state: "active",
|
|
51
|
+
id: state.id,
|
|
52
|
+
handoff: state.handoff,
|
|
53
|
+
prompts: state.prompts,
|
|
54
|
+
}
|
|
55
|
+
: { version: 1, state: "closed", id: state.id },
|
|
56
|
+
);
|
|
57
|
+
} finally {
|
|
58
|
+
if (!state.active) notify();
|
|
59
|
+
}
|
|
60
|
+
if (state.active) notify();
|
|
41
61
|
},
|
|
42
62
|
onPendingChange: (update) => {
|
|
43
63
|
pi.appendEntry(PENDING_ASK_ENTRY_TYPE, update);
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@howaboua/pi-ask",
|
|
3
|
-
"version": "0.0.
|
|
3
|
+
"version": "0.0.9",
|
|
4
4
|
"description": "Interactive decisions, review triage, and human handoffs for Pi.",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"license": "MIT",
|
|
@@ -45,7 +45,7 @@
|
|
|
45
45
|
]
|
|
46
46
|
},
|
|
47
47
|
"peerDependencies": {
|
|
48
|
-
"@howaboua/pi-codex-conversion": ">=3.0.
|
|
48
|
+
"@howaboua/pi-codex-conversion": ">=3.0.33",
|
|
49
49
|
"@earendil-works/pi-ai": ">=0.84.3",
|
|
50
50
|
"@earendil-works/pi-coding-agent": ">=0.84.3",
|
|
51
51
|
"@earendil-works/pi-tui": ">=0.84.3",
|
|
@@ -57,11 +57,11 @@
|
|
|
57
57
|
}
|
|
58
58
|
},
|
|
59
59
|
"devDependencies": {
|
|
60
|
-
"@howaboua/pi-codex-conversion": "3.0.
|
|
60
|
+
"@howaboua/pi-codex-conversion": "3.0.33",
|
|
61
61
|
"@biomejs/biome": "^2.5.10",
|
|
62
|
-
"@earendil-works/pi-ai": "0.85.
|
|
63
|
-
"@earendil-works/pi-coding-agent": "0.85.
|
|
64
|
-
"@earendil-works/pi-tui": "0.85.
|
|
62
|
+
"@earendil-works/pi-ai": "0.85.1",
|
|
63
|
+
"@earendil-works/pi-coding-agent": "0.85.1",
|
|
64
|
+
"@earendil-works/pi-tui": "0.85.1",
|
|
65
65
|
"typebox": "1.3.18",
|
|
66
66
|
"typescript": "^7.0.2"
|
|
67
67
|
},
|