@mrclrchtr/supi-prompt-suggestions 11.2.0 → 11.4.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/CHANGELOG.md
ADDED
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
# Changelog
|
|
2
|
+
|
|
3
|
+
## [11.4.0]
|
|
4
|
+
|
|
5
|
+
- Suggestions now predict your next message, preferring commands such as "push it" over questions, instead of continuing in the assistant's voice. When the assistant asked a question, the suggestion answers it as a command.
|
|
6
|
+
|
|
7
|
+
## [11.3.0]
|
|
8
|
+
|
|
9
|
+
- Added package-specific release notes for model-powered prompt suggestions.
|
|
@@ -0,0 +1,110 @@
|
|
|
1
|
+
// Session-provided Web tools for SuPi child sessions (Agent Runs and Reviewer Sessions).
|
|
2
|
+
//
|
|
3
|
+
// The loaded supi-web extension answers a synchronous request on the parent event bus.
|
|
4
|
+
// Consumers never depend on supi-web; when it is absent or provides no tools, the
|
|
5
|
+
// request returns undefined and the child simply lacks Web tools.
|
|
6
|
+
|
|
7
|
+
import type { ExtensionAPI, ExtensionFactory } from "@earendil-works/pi-coding-agent";
|
|
8
|
+
|
|
9
|
+
/** Web tools that child sessions may receive when the containing session provides them. */
|
|
10
|
+
export const CHILD_WEB_TOOL_NAMES = [
|
|
11
|
+
"web_fetch_md",
|
|
12
|
+
"web_search",
|
|
13
|
+
"web_docs_search",
|
|
14
|
+
"web_docs_fetch",
|
|
15
|
+
] as const;
|
|
16
|
+
|
|
17
|
+
/** One session-provided Web tool name. */
|
|
18
|
+
export type ChildWebToolName = (typeof CHILD_WEB_TOOL_NAMES)[number];
|
|
19
|
+
|
|
20
|
+
/**
|
|
21
|
+
* Web tools currently callable in the containing session plus a child-only extension
|
|
22
|
+
* factory that registers them. The factory must not capture the parent session; it may
|
|
23
|
+
* capture plain data such as the containing session's configuration directory.
|
|
24
|
+
*/
|
|
25
|
+
export interface ChildWebToolProvision {
|
|
26
|
+
readonly toolNames: readonly ChildWebToolName[];
|
|
27
|
+
/** Register the requested subset of `toolNames` in a child session. */
|
|
28
|
+
readonly createFactory: (toolNames: readonly ChildWebToolName[]) => ExtensionFactory;
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
/** Plain containing-session data supplied with a child Web tool request. */
|
|
32
|
+
export interface ChildWebToolsRequestContext {
|
|
33
|
+
/**
|
|
34
|
+
* Working directory whose SuPi configuration governs Web settings such as Firecrawl.
|
|
35
|
+
* Children with a different cwd (for example a frozen Review Workspace) keep this policy.
|
|
36
|
+
*/
|
|
37
|
+
readonly configCwd: string;
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
type EventBusLike = Pick<ExtensionAPI["events"], "on" | "emit">;
|
|
41
|
+
|
|
42
|
+
const REQUEST_CHANNEL = "supi:child-web-tools:request";
|
|
43
|
+
const RESPONSE_CHANNEL = "supi:child-web-tools:response";
|
|
44
|
+
|
|
45
|
+
interface ChildWebToolsRequest extends ChildWebToolsRequestContext {
|
|
46
|
+
readonly requestId: string;
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
interface ChildWebToolsResponse {
|
|
50
|
+
readonly requestId: string;
|
|
51
|
+
readonly provision: ChildWebToolProvision;
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
let nextRequestId = 0;
|
|
55
|
+
|
|
56
|
+
/**
|
|
57
|
+
* Answer child Web tool requests on the parent event bus. The callback must be synchronous
|
|
58
|
+
* because Pi's event bus does not await listeners. Returns an unsubscribe function.
|
|
59
|
+
*/
|
|
60
|
+
export function provideChildWebTools(
|
|
61
|
+
events: EventBusLike,
|
|
62
|
+
resolve: (context: ChildWebToolsRequestContext) => ChildWebToolProvision | undefined,
|
|
63
|
+
): () => void {
|
|
64
|
+
return events.on(REQUEST_CHANNEL, (data) => {
|
|
65
|
+
const request = data as Partial<ChildWebToolsRequest> | undefined;
|
|
66
|
+
if (typeof request?.requestId !== "string" || typeof request.configCwd !== "string") return;
|
|
67
|
+
const { requestId, configCwd } = request;
|
|
68
|
+
const provision = resolve({ configCwd });
|
|
69
|
+
if (!provision || provision.toolNames.length === 0) return;
|
|
70
|
+
events.emit(RESPONSE_CHANNEL, { requestId, provision } satisfies ChildWebToolsResponse);
|
|
71
|
+
});
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
/** Capture the Web tools the containing session currently provides, if any. */
|
|
75
|
+
export function requestChildWebTools(
|
|
76
|
+
events: EventBusLike,
|
|
77
|
+
context: ChildWebToolsRequestContext,
|
|
78
|
+
): ChildWebToolProvision | undefined {
|
|
79
|
+
const requestId = `child-web-${process.pid}-${++nextRequestId}`;
|
|
80
|
+
let provision: ChildWebToolProvision | undefined;
|
|
81
|
+
const unsubscribe = events.on(RESPONSE_CHANNEL, (data) => {
|
|
82
|
+
const response = data as Partial<ChildWebToolsResponse> | undefined;
|
|
83
|
+
if (response?.requestId === requestId && response.provision) provision = response.provision;
|
|
84
|
+
});
|
|
85
|
+
try {
|
|
86
|
+
events.emit(REQUEST_CHANNEL, { requestId, ...context } satisfies ChildWebToolsRequest);
|
|
87
|
+
} finally {
|
|
88
|
+
unsubscribe();
|
|
89
|
+
}
|
|
90
|
+
return provision;
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
/**
|
|
94
|
+
* Return the Web tools callable in a session under Pi's exposure rules: active `direct`
|
|
95
|
+
* tools, plus registered `codemode` and `deferred` tools. `model-only` and `hidden`
|
|
96
|
+
* tools are not callable.
|
|
97
|
+
*/
|
|
98
|
+
export function callableChildWebToolNames(
|
|
99
|
+
pi: Pick<ExtensionAPI, "getAllTools" | "getActiveTools">,
|
|
100
|
+
): ChildWebToolName[] {
|
|
101
|
+
const active = new Set(pi.getActiveTools());
|
|
102
|
+
const exposures = new Map(
|
|
103
|
+
pi.getAllTools().map((tool) => [tool.name, tool.exposure ?? "direct"] as const),
|
|
104
|
+
);
|
|
105
|
+
return CHILD_WEB_TOOL_NAMES.filter((name) => {
|
|
106
|
+
const exposure = exposures.get(name);
|
|
107
|
+
if (exposure === "direct") return active.has(name);
|
|
108
|
+
return exposure === "codemode" || exposure === "deferred";
|
|
109
|
+
});
|
|
110
|
+
}
|
|
@@ -1,5 +1,16 @@
|
|
|
1
1
|
// supi-core session domain — session utilities and registries.
|
|
2
2
|
|
|
3
|
+
export type {
|
|
4
|
+
ChildWebToolName,
|
|
5
|
+
ChildWebToolProvision,
|
|
6
|
+
ChildWebToolsRequestContext,
|
|
7
|
+
} from "./child-web-tools.ts";
|
|
8
|
+
export {
|
|
9
|
+
CHILD_WEB_TOOL_NAMES,
|
|
10
|
+
callableChildWebToolNames,
|
|
11
|
+
provideChildWebTools,
|
|
12
|
+
requestChildWebTools,
|
|
13
|
+
} from "./child-web-tools.ts";
|
|
3
14
|
export { createRegistry, createSessionStateRegistry } from "./registry-utils.ts";
|
|
4
15
|
export type {
|
|
5
16
|
SessionCapabilitySkill,
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@mrclrchtr/supi-prompt-suggestions",
|
|
3
|
-
"version": "11.
|
|
3
|
+
"version": "11.4.0",
|
|
4
4
|
"description": "Optional model-powered ghost-text prompt suggestions",
|
|
5
5
|
"license": "MIT",
|
|
6
6
|
"repository": {
|
|
@@ -27,6 +27,7 @@
|
|
|
27
27
|
],
|
|
28
28
|
"type": "module",
|
|
29
29
|
"files": [
|
|
30
|
+
"CHANGELOG.md",
|
|
30
31
|
"src/**/*.ts",
|
|
31
32
|
"!__tests__"
|
|
32
33
|
],
|
|
@@ -48,7 +49,7 @@
|
|
|
48
49
|
"video": "https://raw.githubusercontent.com/mrclrchtr/supi/main/packages/supi-prompt-suggestions/assets/demo.mp4"
|
|
49
50
|
},
|
|
50
51
|
"dependencies": {
|
|
51
|
-
"@mrclrchtr/supi-core": "11.
|
|
52
|
+
"@mrclrchtr/supi-core": "11.4.0"
|
|
52
53
|
},
|
|
53
54
|
"bundledDependencies": [
|
|
54
55
|
"@mrclrchtr/supi-core"
|
package/src/generation/client.ts
CHANGED
|
@@ -14,28 +14,38 @@ import {
|
|
|
14
14
|
export const GENERATION_TIMEOUT_MS = 20_000;
|
|
15
15
|
|
|
16
16
|
/**
|
|
17
|
-
* System prompt instructs the model to
|
|
18
|
-
*
|
|
19
|
-
* when none is useful.
|
|
17
|
+
* System prompt instructs the model to predict the user's next message,
|
|
18
|
+
* preferably an imperative command to the assistant, or return the
|
|
19
|
+
* NO_SUGGESTION sentinel when none is useful.
|
|
20
20
|
*
|
|
21
21
|
* The model receives no PI, SuPi, project, or conversation context —
|
|
22
22
|
* only the last assistant message text is sent.
|
|
23
23
|
*/
|
|
24
|
-
const SYSTEM_PROMPT =
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
24
|
+
const SYSTEM_PROMPT = `You predict the next message a developer types to their coding assistant.
|
|
25
|
+
You receive the assistant's last message. Write the single line the USER would send next.
|
|
26
|
+
|
|
27
|
+
Rules:
|
|
28
|
+
- Write as the user talking to the assistant. Never write in the assistant's voice and never ask the user anything (no "Should I…?", "Do you want…?", "Shall we…?").
|
|
29
|
+
- Prefer a short imperative command that moves the work forward: "Push the branch.", "Run the tests.", "Fix the lint errors."
|
|
30
|
+
- If the assistant asks a question or offers options, answer it as a command that picks the most sensible option, e.g. "Yes, push it to origin." or "Use option 2."
|
|
31
|
+
- Name concrete targets from the message (branch, file, command, option) when they make the command unambiguous.
|
|
32
|
+
- Ask a question only when the user clearly needs information before deciding.
|
|
33
|
+
- No greetings, thanks, politeness, explanations, quotes, or prefixes like "Suggestion:".
|
|
34
|
+
- Keep it under 120 characters.
|
|
35
|
+
- If no useful next message exists, respond with exactly NO_SUGGESTION.
|
|
36
|
+
|
|
37
|
+
Examples (assistant message → your output):
|
|
38
|
+
"Committed as abc123. Not pushed: the branch is 3 commits ahead of origin." → Push the branch to origin.
|
|
39
|
+
"Should I also update the README, or keep the change code-only?" → Update the README too.
|
|
40
|
+
"Two options: A) add a cache, B) batch the requests. Which do you prefer?" → Go with B and batch the requests.
|
|
41
|
+
|
|
42
|
+
Output only the message text.`;
|
|
33
43
|
|
|
34
44
|
// ── Prompt building ────────────────────────────────────────────────────────
|
|
35
45
|
|
|
36
46
|
/** Format the tail text as a completion prompt. */
|
|
37
47
|
export function buildPrompt(tail: string): string {
|
|
38
|
-
return `<assistant_message>\n${tail}\n</assistant_message>\n\
|
|
48
|
+
return `<assistant_message>\n${tail}\n</assistant_message>\n\nUser's next message:`;
|
|
39
49
|
}
|
|
40
50
|
|
|
41
51
|
// ── Types ───────────────────────────────────────────────────────────────────
|