@evandu/super-pi 0.1.39 → 0.1.41
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/super-pi-task/index.ts +29 -1
- package/dist/client/assets/{index-DX1yjXR8.css → index-DTd4g0h5.css} +1 -1
- package/dist/client/assets/{index-DSJuo0Bi.js → index-Dihs8Ezc.js} +2 -1
- package/dist/client/index.html +2 -2
- package/dist/server/application/run-service.d.ts +26 -10
- package/dist/server/application/run-service.js +99 -21
- package/dist/server/http-server.js +1 -1
- package/dist/server/runtime.js +6 -2
- package/dist/server/store.d.ts +13 -7
- package/dist/server/store.js +22 -31
- package/dist/server/tool-calls.d.ts +16 -1
- package/dist/server/tool-calls.js +58 -0
- package/dist/server/types.d.ts +39 -14
- package/package.json +1 -1
|
@@ -5,6 +5,7 @@ import {
|
|
|
5
5
|
executeTask,
|
|
6
6
|
readTaskConfig,
|
|
7
7
|
type TaskParams,
|
|
8
|
+
type TaskToolResult,
|
|
8
9
|
} from "./core.ts";
|
|
9
10
|
import {
|
|
10
11
|
WORKFLOW_DESCRIPTION,
|
|
@@ -110,7 +111,34 @@ const WorkflowParameters = Type.Object({
|
|
|
110
111
|
type ExtensionApi = { registerTool(tool: ExtensionToolDefinition): void };
|
|
111
112
|
|
|
112
113
|
const extension = (pi: ExtensionApi): void => {
|
|
113
|
-
|
|
114
|
+
// THE QUESTION TOOL. It blocks on an HTTP request until the person answers, and the answer comes back
|
|
115
|
+
// as the tool result, which is what keeps the run alive across the exchange - the model does not decide
|
|
116
|
+
// to continue, it cannot: the tool has not returned.
|
|
117
|
+
//
|
|
118
|
+
// IT RETURNS THE SHAPE EVERY OTHER TOOL HERE RETURNS, AND THAT IS NOT COSMETIC. `TaskToolResult` is
|
|
119
|
+
// `{content: [{type:"text", text}], details}` - `todo`, `task` and `workflow` all build it through the
|
|
120
|
+
// local `text()` helper. This tool used to `return` the parsed JSON object bare, which Pi does not read
|
|
121
|
+
// as a tool result at all: the model received an EMPTY result and said so - 「我无法复述你的答案,
|
|
122
|
+
// question 工具调用后返回了空结果」. The answer was in the store the whole time; it simply never arrived.
|
|
123
|
+
//
|
|
124
|
+
// A REFUSED QUESTION THROWS, and that is the whole of the dismissal semantics: a failed call is what
|
|
125
|
+
// stops the run. OpenCode is the reference and is explicit - rejecting a question fails the deferred
|
|
126
|
+
// and the tool dies (`packages/core/src/question.ts:128-141`). Nothing is invented to fill the gap, not
|
|
127
|
+
// `options[0]` and not an empty selection, which would look to the model exactly like a real choice.
|
|
128
|
+
pi.registerTool({ name: "question", label: "Question", description: "Ask the user a blocking single or multiple choice question with an optional extra note.", parameters: QuestionParameters, execute: async (_id, raw): Promise<TaskToolResult> => {
|
|
129
|
+
const endpoint = process.env.SUPER_PI_QUESTION_ENDPOINT;
|
|
130
|
+
const conversationId = process.env.SUPER_PI_TASK_CONVERSATION;
|
|
131
|
+
const text = (body: string, isError?: boolean): TaskToolResult => ({ content: [{ type: "text", text: body }], details: undefined, ...(isError ? { isError: true } : {}) });
|
|
132
|
+
if (!endpoint || !conversationId) return text("This run cannot ask the user anything: the question endpoint is not available.", true);
|
|
133
|
+
const p = raw as { question: string; options: string[]; multiple?: boolean; extraNote?: boolean };
|
|
134
|
+
const response = await fetch(endpoint, { method: "POST", headers: { "content-type": "application/json" }, body: JSON.stringify({ conversationId, ...p }) });
|
|
135
|
+
const payload = await response.json() as { error?: string; answers?: { id: string; selected: string[]; note?: string }[]; summary?: string };
|
|
136
|
+
if (!response.ok || payload?.error) return text(payload?.error ?? `The question could not be delivered (HTTP ${response.status}).`, true);
|
|
137
|
+
// READABLE, NOT JUST PRESENT. The bare `{answers:[{id, selected}]}` carries a UUID and an array, and
|
|
138
|
+
// a model handed that has to guess what it means - which it did, describing a real answer as a
|
|
139
|
+
// cancellation. The text says what was asked and what was chosen, in words.
|
|
140
|
+
return text(JSON.stringify({ answers: (payload.answers ?? []).map((answer) => ({ question: p.question, selected: answer.selected, ...(answer.note ? { note: answer.note } : {}) })), ...(payload.summary ? { summary: payload.summary } : {}) }));
|
|
141
|
+
} });
|
|
114
142
|
// Read ONCE at load, not per call: the environment cannot change under a running Pi, and `prepareLoadout`
|
|
115
143
|
// is synchronous - it cannot await a fetch for a roster - so the roster has to already be here.
|
|
116
144
|
const config = readTaskConfig(process.env);
|