@evandu/super-pi 0.1.128 → 0.1.129
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-knowledge/index.ts +4 -3
- package/agents/super-pi-memory/index.ts +5 -4
- package/agents/super-pi-task/core.ts +8 -7
- package/dist/.super-pi-check-inputs.json +2 -2
- package/dist/client/.super-pi-client-sources.json +5 -5
- package/dist/client/assets/{CodeMirrorEditor-BhxCTVTL.js → CodeMirrorEditor-BNVBZZSS.js} +2 -2
- package/dist/client/assets/{dist-DERv1ty2.js → dist-B2nPVdKh.js} +1 -1
- package/dist/client/assets/{dist-BAmT9jUI.js → dist-B61RSa-K.js} +1 -1
- package/dist/client/assets/{dist-BPBlgusB.js → dist-BBW2SE_R.js} +1 -1
- package/dist/client/assets/{dist-BQK4xccN.js → dist-BDygQgZD.js} +1 -1
- package/dist/client/assets/{dist-C-hIztMW.js → dist-BKp4AWEJ.js} +1 -1
- package/dist/client/assets/{dist-Cl87QYqE.js → dist-BhJGK8-b.js} +1 -1
- package/dist/client/assets/{dist-Bplw4N5j.js → dist-BykVFExd.js} +1 -1
- package/dist/client/assets/{dist-K96Y1Tut.js → dist-C90MPTnk.js} +1 -1
- package/dist/client/assets/{dist-DVBpWkDV.js → dist-CMnvChDX.js} +1 -1
- package/dist/client/assets/{dist-BfwwrK33.js → dist-CRzRB9k3.js} +1 -1
- package/dist/client/assets/{dist-BeYwU1xB.js → dist-CWYRjnj-.js} +1 -1
- package/dist/client/assets/{dist-AtHUPHQe.js → dist-CZ9I2JCd.js} +1 -1
- package/dist/client/assets/{dist-8-xcbQPp.js → dist-ChszWGDv.js} +1 -1
- package/dist/client/assets/{dist-Bof_dYLf.js → dist-CiSGSVBZ.js} +1 -1
- package/dist/client/assets/{dist-Dc6xSTJL.js → dist-Cxerc7Bb.js} +1 -1
- package/dist/client/assets/{dist-DobX-p1U.js → dist-DB5M5fZG.js} +1 -1
- package/dist/client/assets/{dist-YUFYdtGJ.js → dist-DZnUERoW.js} +1 -1
- package/dist/client/assets/{dist-BJZUzzzf.js → dist-DmAOqr0M.js} +1 -1
- package/dist/client/assets/{dist-Bw8QOdGP.js → dist-Dq4-QFAJ.js} +1 -1
- package/dist/client/assets/{dist-icIMVISS.js → dist-GkIV1LTX.js} +1 -1
- package/dist/client/assets/{dist-DTXHzd8a.js → dist-HdeIBl2J.js} +1 -1
- package/dist/client/assets/{dist-5LsaarOt.js → dist-Ur37340K.js} +1 -1
- package/dist/client/assets/index-CXB6U5mz.css +1 -0
- package/dist/client/assets/{index-BvQ3umhR.js → index-D_c-CYAr.js} +35 -37
- package/dist/client/index.html +2 -2
- package/dist/server/active-memory.d.ts +8 -3
- package/dist/server/active-memory.js +44 -1
- package/dist/server/application/run-service.js +10 -1
- package/dist/server/git.d.ts +87 -0
- package/dist/server/git.js +301 -2
- package/dist/server/http-server.js +39 -7
- package/dist/server/pi-session.d.ts +9 -0
- package/dist/server/pi-session.js +12 -3
- package/dist/server/runtime.d.ts +12 -0
- package/dist/server/runtime.js +6 -0
- package/dist/server/store.js +16 -5
- package/dist/server/ws-server.js +3 -0
- package/package.json +2 -1
- package/scripts/diagnose.mjs +8 -3
- package/dist/client/assets/index-B8GInEf1.css +0 -1
|
@@ -210,6 +210,15 @@ export interface PiSessionLike {
|
|
|
210
210
|
* message for later", and the caller falls back to answering it as a turn of its own.
|
|
211
211
|
*/
|
|
212
212
|
followUp?(message: string, images?: unknown[]): Promise<"queued" | "handled">;
|
|
213
|
+
sendCustomMessage?(message: {
|
|
214
|
+
customType: string;
|
|
215
|
+
content: unknown;
|
|
216
|
+
display?: string;
|
|
217
|
+
details?: unknown;
|
|
218
|
+
}, options?: {
|
|
219
|
+
triggerTurn?: boolean;
|
|
220
|
+
deliverAs?: "steer" | "followUp" | "nextTurn";
|
|
221
|
+
}): Promise<void>;
|
|
213
222
|
/**
|
|
214
223
|
* Removes every message Pi is holding in its steering and follow-up queues, and returns what was
|
|
215
224
|
* removed.
|
|
@@ -231,7 +231,15 @@ export async function createPiSession(creation) {
|
|
|
231
231
|
const sdk = await loadSdk();
|
|
232
232
|
traceSession(creation.sessionId, "sdk.load.end", startedAt);
|
|
233
233
|
const agentDir = creation.agentDir ?? defaultAgentDir();
|
|
234
|
-
|
|
234
|
+
// Extension credentials are per-session capabilities. Do not temporarily mutate process.env here:
|
|
235
|
+
// two concurrent session creations otherwise race and can capture each other's capability.
|
|
236
|
+
const inlinePaths = creation.extensionPaths.filter((path) => /super-pi-(task|memory|knowledge)/.test(path.replaceAll("\\", "/")));
|
|
237
|
+
const externalPaths = creation.extensionPaths.filter((path) => !inlinePaths.includes(path));
|
|
238
|
+
const extensionFactories = await Promise.all(inlinePaths.map(async (path) => {
|
|
239
|
+
const mod = await import(pathToFileURL(path).href);
|
|
240
|
+
return async (pi) => mod.default?.(pi, creation.extensionEnv);
|
|
241
|
+
}));
|
|
242
|
+
return (async () => {
|
|
235
243
|
// THE AGENT'S PROMPT. Pi's loader takes the same two shapes the command line took: a `systemPrompt`
|
|
236
244
|
// that REPLACES Pi's own, and an `appendSystemPrompt` list of files that are ADDED to it. An agent
|
|
237
245
|
// that says `promptMode: replace` gets the first, and one that says nothing gets the second - the
|
|
@@ -241,7 +249,8 @@ export async function createPiSession(creation) {
|
|
|
241
249
|
const loader = new sdk.DefaultResourceLoader({
|
|
242
250
|
cwd: creation.cwd,
|
|
243
251
|
agentDir,
|
|
244
|
-
...(
|
|
252
|
+
...(externalPaths.length ? { additionalExtensionPaths: externalPaths } : {}),
|
|
253
|
+
...(extensionFactories.length ? { extensionFactories } : {}),
|
|
245
254
|
...(creation.systemPromptFile && creation.systemPromptMode === "replace"
|
|
246
255
|
? { systemPrompt: creation.systemPromptFile }
|
|
247
256
|
: creation.systemPromptFile
|
|
@@ -298,7 +307,7 @@ export async function createPiSession(creation) {
|
|
|
298
307
|
return found;
|
|
299
308
|
};
|
|
300
309
|
return live;
|
|
301
|
-
});
|
|
310
|
+
})();
|
|
302
311
|
}
|
|
303
312
|
function transcriptBytes(sessionManager) {
|
|
304
313
|
const file = sessionManager.getSessionFile?.();
|
package/dist/server/runtime.d.ts
CHANGED
|
@@ -272,6 +272,12 @@ export interface AgentRuntime {
|
|
|
272
272
|
* own waiting list - Pi is holding a copy, not the only copy.
|
|
273
273
|
*/
|
|
274
274
|
followUp?(conversationId: string, message: string, images?: MessageImage[]): Promise<SteerDisposition>;
|
|
275
|
+
sendCustomMessage?(conversationId: string, message: {
|
|
276
|
+
customType: string;
|
|
277
|
+
content: string;
|
|
278
|
+
display?: string;
|
|
279
|
+
details: Record<string, unknown>;
|
|
280
|
+
}): Promise<void>;
|
|
275
281
|
/**
|
|
276
282
|
* Empties Pi's steering and follow-up queues, and returns what was in them.
|
|
277
283
|
*
|
|
@@ -1067,6 +1073,12 @@ export declare class PiRpcRuntime implements AgentRuntime {
|
|
|
1067
1073
|
* was busy means).
|
|
1068
1074
|
*/
|
|
1069
1075
|
followUp(conversationId: string, message: string, images?: MessageImage[]): Promise<SteerDisposition>;
|
|
1076
|
+
sendCustomMessage(conversationId: string, message: {
|
|
1077
|
+
customType: string;
|
|
1078
|
+
content: string;
|
|
1079
|
+
display?: string;
|
|
1080
|
+
details: Record<string, unknown>;
|
|
1081
|
+
}): Promise<void>;
|
|
1070
1082
|
/**
|
|
1071
1083
|
* Empties Pi's steering and follow-up queues and returns what was in them.
|
|
1072
1084
|
*
|
package/dist/server/runtime.js
CHANGED
|
@@ -2184,6 +2184,12 @@ export class PiRpcRuntime {
|
|
|
2184
2184
|
// do anyway.
|
|
2185
2185
|
throw new SteerUnavailableError(`会话 ${conversationId} 当前没有可以接手的 Pi 会话,无法排队`);
|
|
2186
2186
|
}
|
|
2187
|
+
async sendCustomMessage(conversationId, message) {
|
|
2188
|
+
const session = await this.sessionFor(conversationId);
|
|
2189
|
+
if (!session.sendCustomMessage)
|
|
2190
|
+
throw new SteerUnavailableError(`会话 ${conversationId} 的 Pi 不支持 custom message`);
|
|
2191
|
+
await session.sendCustomMessage(message, { triggerTurn: true, deliverAs: "followUp" });
|
|
2192
|
+
}
|
|
2187
2193
|
/**
|
|
2188
2194
|
* Empties Pi's steering and follow-up queues and returns what was in them.
|
|
2189
2195
|
*
|
package/dist/server/store.js
CHANGED
|
@@ -636,13 +636,17 @@ export class Store {
|
|
|
636
636
|
console.warn(`Super Pi: state file ${repository.filePath} could not be loaded (${reason}) and could not be moved aside either (${moveError instanceof Error ? moveError.message : String(moveError)}) - the server starts with a fresh state; the unreadable file is still at ${repository.filePath}.`);
|
|
637
637
|
}
|
|
638
638
|
}
|
|
639
|
-
const createdAt = now();
|
|
640
639
|
// NO WRITE HERE, AND THAT IS AN INVARIANT RATHER THAN AN OMISSION: a store with no file behind it
|
|
641
640
|
// stays lazy, so opening one never creates a file and never has to un-create one. `persistence-
|
|
642
641
|
// repository.test.mjs` asserts it by reading the path and expecting ENOENT. Repairing an EXISTING
|
|
643
642
|
// file is the other case and is handled above; this branch invented its contents and says nothing
|
|
644
643
|
// until something actually happens.
|
|
645
|
-
|
|
644
|
+
//
|
|
645
|
+
// A fresh state starts with NO workspace, and that is a constraint rather than a gap: this branch
|
|
646
|
+
// used to seed one named after process.cwd(), so whatever directory the server happened to be
|
|
647
|
+
// launched from silently became a workspace. The first workspace is always an explicit decision -
|
|
648
|
+
// the client's zero-workspace empty state (App.tsx) asks for one instead.
|
|
649
|
+
return new Store(repository, { workspaces: [], conversations: [], subagentRuns: [], backgroundTasks: [] });
|
|
646
650
|
}
|
|
647
651
|
}
|
|
648
652
|
async setModelFilterRules(rules) {
|
|
@@ -697,7 +701,13 @@ export class Store {
|
|
|
697
701
|
throw error;
|
|
698
702
|
}
|
|
699
703
|
}
|
|
700
|
-
snapshot() {
|
|
704
|
+
snapshot() {
|
|
705
|
+
const startedAt = process.env.SUPER_PI_TRACE_LATENCY === "1" ? performance.now() : 0;
|
|
706
|
+
const value = structuredClone(this.state);
|
|
707
|
+
if (startedAt)
|
|
708
|
+
console.warn(`[super-pi latency] phase=state.snapshot bytes=${JSON.stringify(value).length} cloneMs=${Math.round(performance.now() - startedAt)}`);
|
|
709
|
+
return value;
|
|
710
|
+
}
|
|
701
711
|
/**
|
|
702
712
|
* Whether a conversation already holds a message with this id.
|
|
703
713
|
*
|
|
@@ -796,8 +806,9 @@ export class Store {
|
|
|
796
806
|
async deleteWorkspace(workspaceId) {
|
|
797
807
|
if (!this.state.workspaces.some((item) => item.id === workspaceId))
|
|
798
808
|
throw new Error("找不到这个工作区。");
|
|
799
|
-
|
|
800
|
-
|
|
809
|
+
// Deleting the LAST workspace is allowed: a fresh store starts with zero (see `openRepository`), so
|
|
810
|
+
// "at least one" was an artifact of the old first-run seed, and the client's zero-workspace empty
|
|
811
|
+
// state is the onboarding path back in.
|
|
801
812
|
// Cascade: workspaces and conversations live in one state.json, so this single atomic persist removes
|
|
802
813
|
// both together. A waiting message needs no sweep of its own any more - it is a message inside one of
|
|
803
814
|
// the conversations being removed, so it goes with its container, which is the whole reason it is a
|
package/dist/server/ws-server.js
CHANGED
|
@@ -73,7 +73,10 @@ export class WsBroadcaster {
|
|
|
73
73
|
* Stream deltas stay per-event: they are coalesced per socket, so there is no single payload to
|
|
74
74
|
* share, and their cost is one small string.
|
|
75
75
|
*/
|
|
76
|
+
const startedAt = performance.now();
|
|
76
77
|
const payload = streaming ? undefined : JSON.stringify(event);
|
|
78
|
+
if (!streaming && event.type === "state" && process.env.SUPER_PI_TRACE_LATENCY === "1")
|
|
79
|
+
console.warn(`[super-pi latency] phase=state.broadcast bytes=${payload?.length ?? 0} sockets=${this.sockets.size} serializeMs=${Math.round(performance.now() - startedAt)}`);
|
|
77
80
|
for (const socket of this.sockets) {
|
|
78
81
|
if (socket.readyState !== WebSocket.OPEN)
|
|
79
82
|
continue;
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@evandu/super-pi",
|
|
3
|
-
"version": "0.1.
|
|
3
|
+
"version": "0.1.129",
|
|
4
4
|
"description": "Web control plane and companion for Pi",
|
|
5
5
|
"license": "MIT",
|
|
6
6
|
"type": "module",
|
|
@@ -101,6 +101,7 @@
|
|
|
101
101
|
"scripts/send-retry.test.mjs",
|
|
102
102
|
"scripts/error-messages.test.mjs",
|
|
103
103
|
"scripts/git-diff-view.test.mjs",
|
|
104
|
+
"scripts/git-blame.test.mjs",
|
|
104
105
|
"scripts/fork-server.test.mjs",
|
|
105
106
|
"scripts/styles-no-dangling.test.mjs",
|
|
106
107
|
"scripts/theme-no-hardcoded-colours.test.mjs",
|
package/scripts/diagnose.mjs
CHANGED
|
@@ -229,8 +229,13 @@ export async function diagnose({ repositoryRoot, turns = 2, onProgress = () => {
|
|
|
229
229
|
throw new Error(`timed out waiting for ${label}`);
|
|
230
230
|
};
|
|
231
231
|
|
|
232
|
+
// A fresh store starts with no workspace, so the diagnosis creates the one it measures against -
|
|
233
|
+
// before any measured span, so workspace setup never lands inside a number.
|
|
234
|
+
send({ type: "workspace.create", name: "diagnose", path: process.cwd() });
|
|
235
|
+
const workspaceId = (await waitFor((e) => e.type === "state" && e.state.workspaces.some((w) => w.name === "diagnose"), "the diagnose workspace")).state.workspaces.find((w) => w.name === "diagnose").id;
|
|
236
|
+
|
|
232
237
|
onProgress("creating a conversation...");
|
|
233
|
-
send({ type: "conversation.create", workspaceId
|
|
238
|
+
send({ type: "conversation.create", workspaceId });
|
|
234
239
|
await waitFor((e) => e.type === "state" && e.state.conversations.length > 0, "a conversation");
|
|
235
240
|
const conversation = events.filter((e) => e.type === "state").at(-1).state.conversations[0];
|
|
236
241
|
|
|
@@ -258,13 +263,13 @@ export async function diagnose({ repositoryRoot, turns = 2, onProgress = () => {
|
|
|
258
263
|
onProgress("setting a workspace default model, then creating a conversation...");
|
|
259
264
|
send({
|
|
260
265
|
type: "workspace.settings",
|
|
261
|
-
workspaceId
|
|
266
|
+
workspaceId,
|
|
262
267
|
model: { provider: defaultModel.provider, modelId: defaultModel.modelId },
|
|
263
268
|
thinkingLevel: null,
|
|
264
269
|
});
|
|
265
270
|
await sleep(400);
|
|
266
271
|
const withDefaultStart = Date.now();
|
|
267
|
-
send({ type: "conversation.create", workspaceId
|
|
272
|
+
send({ type: "conversation.create", workspaceId });
|
|
268
273
|
const withDefault = await waitFor((e) => e.type === "state"
|
|
269
274
|
&& e.state.conversations.length >= 2, "a conversation with the workspace default");
|
|
270
275
|
result.conversationWithDefaultMs = Date.now() - withDefaultStart;
|