@evandu/super-pi 0.1.141 → 0.1.142
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/dist/.super-pi-check-inputs.json +2 -2
- package/dist/client/.super-pi-client-sources.json +5 -6
- package/dist/client/assets/{CodeMirrorEditor-Dm5IDKy9.js → CodeMirrorEditor-iLUdQy9q.js} +2 -2
- package/dist/client/assets/{dist-DtO-7WEZ.js → dist-0dCNvYEE.js} +1 -1
- package/dist/client/assets/{dist-DQWPdnen.js → dist-B4JqBNQI.js} +1 -1
- package/dist/client/assets/{dist-D0-9TI_k.js → dist-B8FbKhnf.js} +1 -1
- package/dist/client/assets/{dist-4baaMdqn.js → dist-BA6EsWzo.js} +1 -1
- package/dist/client/assets/{dist-BGaKp7BG.js → dist-Bj2C-QqM.js} +1 -1
- package/dist/client/assets/{dist-LCDxbTTI.js → dist-Bt8G7e7e.js} +1 -1
- package/dist/client/assets/{dist-B-edBMDa.js → dist-CWBm3q4a.js} +1 -1
- package/dist/client/assets/{dist-D7YZUqfA.js → dist-Cfa_e2VZ.js} +1 -1
- package/dist/client/assets/{dist-GAPsnCM8.js → dist-CoTJns5x.js} +1 -1
- package/dist/client/assets/{dist-BqXU6S8h.js → dist-CtCiGxne.js} +1 -1
- package/dist/client/assets/{dist-D0eIQjoI.js → dist-DBAoCOnn.js} +1 -1
- package/dist/client/assets/{dist-CNgQgdyS.js → dist-DEUl3Yqk.js} +1 -1
- package/dist/client/assets/{dist-F7KOq8Xm.js → dist-DHyWJ9kA.js} +1 -1
- package/dist/client/assets/{dist-BMbwbGH9.js → dist-DbLEngKN.js} +1 -1
- package/dist/client/assets/{dist-CMDW84Eh.js → dist-Df9zcCXs.js} +1 -1
- package/dist/client/assets/{dist-CN1j5LO2.js → dist-Dq6y_nK3.js} +1 -1
- package/dist/client/assets/{dist-BvCNiV_z.js → dist-Dxq1s4j3.js} +1 -1
- package/dist/client/assets/{dist-C7UQGfmz.js → dist-F634gv7V.js} +1 -1
- package/dist/client/assets/{dist-PQZBepmy.js → dist-JuLMNOUt.js} +1 -1
- package/dist/client/assets/{dist-CSDLiQKt.js → dist-Nt-BWmBB.js} +1 -1
- package/dist/client/assets/{dist-DWj0wg8M.js → dist-ic-MopV2.js} +1 -1
- package/dist/client/assets/{dist-M3BFsjal.js → dist-rIYPNzbY.js} +1 -1
- package/dist/client/assets/{index-DHsjaI7m.js → index-CX5gWDve.js} +12 -12
- package/dist/client/assets/{index-BJIfLtDs.css → index-DMhigTiY.css} +1 -1
- package/dist/client/index.html +2 -2
- package/dist/server/application/conversation-service.d.ts +2 -1
- package/dist/server/application/conversation-service.js +25 -7
- package/dist/server/store.d.ts +12 -1
- package/dist/server/store.js +70 -3
- package/dist/server/types.d.ts +3 -0
- package/package.json +325 -316
package/dist/client/index.html
CHANGED
|
@@ -8,8 +8,8 @@
|
|
|
8
8
|
<link rel="alternate icon" href="/favicon.ico" />
|
|
9
9
|
<link rel="apple-touch-icon" href="/favicon.svg" />
|
|
10
10
|
<title>Super Pi</title>
|
|
11
|
-
<script type="module" crossorigin src="/assets/index-
|
|
12
|
-
<link rel="stylesheet" crossorigin href="/assets/index-
|
|
11
|
+
<script type="module" crossorigin src="/assets/index-CX5gWDve.js"></script>
|
|
12
|
+
<link rel="stylesheet" crossorigin href="/assets/index-DMhigTiY.css">
|
|
13
13
|
</head>
|
|
14
14
|
<body>
|
|
15
15
|
<div id="root"></div>
|
|
@@ -74,7 +74,8 @@ export declare class ConversationService {
|
|
|
74
74
|
*/
|
|
75
75
|
archiveConversation(conversationId: string): Promise<void>;
|
|
76
76
|
/**
|
|
77
|
-
* Unarchives
|
|
77
|
+
* Unarchives an archived tree root and every archived descendant. Descendants that are
|
|
78
|
+
* already active need no write; they are already in the live tree this restore rejoins.
|
|
78
79
|
*/
|
|
79
80
|
unarchiveConversation(conversationId: string): Promise<void>;
|
|
80
81
|
/**
|
|
@@ -1,5 +1,11 @@
|
|
|
1
1
|
import { assertModelAllowed } from "../model-filter.js";
|
|
2
2
|
import { newConversationId } from "../store.js";
|
|
3
|
+
function isMissingPrimaryAgent(error, agentId) {
|
|
4
|
+
const message = error instanceof Error ? error.message : String(error);
|
|
5
|
+
const id = JSON.stringify(agentId);
|
|
6
|
+
return message.includes(`Agent ${id} 已失效或不是可用的 primary agent`)
|
|
7
|
+
|| message.includes(`Agent ${id} 不存在或不是可用的 primary agent`);
|
|
8
|
+
}
|
|
3
9
|
/**
|
|
4
10
|
* Service managing conversation lifecycle: creation, selection, archiving,
|
|
5
11
|
* model/thinking/agent configuration, message reversion, and session forking.
|
|
@@ -104,12 +110,23 @@ export class ConversationService {
|
|
|
104
110
|
thinkingLevel = undefined;
|
|
105
111
|
}
|
|
106
112
|
}
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
113
|
+
let agentNotice;
|
|
114
|
+
let resolved = { model, thinkingLevel };
|
|
115
|
+
if (workspace.defaultAgentId) {
|
|
116
|
+
try {
|
|
117
|
+
resolved = await this.resolveAgentConfiguration({ id: workspaceId, model, thinkingLevel }, workspace.defaultAgentId, workspaceId);
|
|
118
|
+
}
|
|
119
|
+
catch (error) {
|
|
120
|
+
// Only an absent definition is downgraded. A present agent whose model, thinking level or
|
|
121
|
+
// workspace rules are invalid must still refuse rather than silently dropping its configuration.
|
|
122
|
+
if (!isMissingPrimaryAgent(error, workspace.defaultAgentId))
|
|
123
|
+
throw error;
|
|
124
|
+
agentNotice = `你设的默认 Agent「${workspace.defaultAgentId}」已经不存在了,这个会话改用 Pi 默认 Agent。请在工作区设置里重新选择默认 Agent。`;
|
|
125
|
+
}
|
|
126
|
+
}
|
|
110
127
|
// `null` is intentional here: Store uses it to remove a workspace value that it
|
|
111
128
|
// would otherwise have copied before applying the resolved configuration.
|
|
112
|
-
const defaults = { ...resolved, model: resolved.model ?? null, thinkingLevel: resolved.thinkingLevel ?? null, ...(modelNotice ? { modelNotice } : {}), ...(parentage ? { parentConversationId: parentage.parentConversationId, origin: parentage.origin } : {}) };
|
|
129
|
+
const defaults = { ...resolved, agentId: resolved.agentId ?? null, model: resolved.model ?? null, thinkingLevel: resolved.thinkingLevel ?? null, ...(modelNotice ? { modelNotice } : {}), ...(agentNotice ? { agentNotice } : {}), ...(parentage ? { parentConversationId: parentage.parentConversationId, origin: parentage.origin } : {}) };
|
|
113
130
|
const created = await this.store.createConversation(workspaceId, defaults);
|
|
114
131
|
this.broadcaster.broadcastState();
|
|
115
132
|
return created.id;
|
|
@@ -169,14 +186,15 @@ export class ConversationService {
|
|
|
169
186
|
*/
|
|
170
187
|
async archiveConversation(conversationId) {
|
|
171
188
|
this.runService.reviewMemoryBoundary(conversationId);
|
|
172
|
-
await this.store.
|
|
189
|
+
await this.store.archiveConversationTree(conversationId);
|
|
173
190
|
this.broadcaster.broadcastState();
|
|
174
191
|
}
|
|
175
192
|
/**
|
|
176
|
-
* Unarchives
|
|
193
|
+
* Unarchives an archived tree root and every archived descendant. Descendants that are
|
|
194
|
+
* already active need no write; they are already in the live tree this restore rejoins.
|
|
177
195
|
*/
|
|
178
196
|
async unarchiveConversation(conversationId) {
|
|
179
|
-
await this.store.
|
|
197
|
+
await this.store.unarchiveConversationTree(conversationId);
|
|
180
198
|
this.broadcaster.broadcastState();
|
|
181
199
|
}
|
|
182
200
|
/**
|
package/dist/server/store.d.ts
CHANGED
|
@@ -266,10 +266,11 @@ export declare class Store {
|
|
|
266
266
|
conversationId?: string;
|
|
267
267
|
}): AppState;
|
|
268
268
|
createConversation(workspaceId: string, configuration?: {
|
|
269
|
-
agentId?: string;
|
|
269
|
+
agentId?: string | null;
|
|
270
270
|
model?: ModelSelection | null;
|
|
271
271
|
thinkingLevel?: string | null;
|
|
272
272
|
modelNotice?: string;
|
|
273
|
+
agentNotice?: string;
|
|
273
274
|
parentConversationId?: string;
|
|
274
275
|
origin?: "user" | "orchestrator";
|
|
275
276
|
}): Promise<Conversation>;
|
|
@@ -341,6 +342,16 @@ export declare class Store {
|
|
|
341
342
|
/** Restoring deletes the key rather than setting it to null, so a fully restored conversation serializes
|
|
342
343
|
* byte-identically to one that was never archived. `delete` is legal here because the field is optional. */
|
|
343
344
|
unarchiveConversation(conversationId: string): Promise<void>;
|
|
345
|
+
/**
|
|
346
|
+
* Archives one whole tree as one state write. Only a conversation with no existing parent may
|
|
347
|
+
* start it; an archived parent is still a parent, so its active descendants cannot be archived
|
|
348
|
+
* independently. All running checks happen before the first mutation, making rejection all-or-none.
|
|
349
|
+
*/
|
|
350
|
+
archiveConversationTree(conversationId: string): Promise<void>;
|
|
351
|
+
/** Restores one archived tree in one write; active descendants are already restored by definition. */
|
|
352
|
+
unarchiveConversationTree(conversationId: string): Promise<void>;
|
|
353
|
+
/** Pre-order traversal with cycle protection; archived nodes are traversed, not treated as leaves. */
|
|
354
|
+
private conversationSubtree;
|
|
344
355
|
/**
|
|
345
356
|
* Rename edits the title and NOTHING else. In particular `updatedAt` is deliberately not touched,
|
|
346
357
|
* for exactly the reason archive is: `updatedAt` is the sidebar's recency key, so bumping it would
|
package/dist/server/store.js
CHANGED
|
@@ -1060,7 +1060,7 @@ export class Store {
|
|
|
1060
1060
|
// here and never consulted again: the session picker edits the conversation's own fields, so an
|
|
1061
1061
|
// existing conversation is untouched by later workspace settings, and a workspace edit never
|
|
1062
1062
|
// rewrites history. Field by field, never a spread, for the same reason setConversationModel
|
|
1063
|
-
// narrows:
|
|
1063
|
+
// narrows: every inherited field has an explicit birth override, including null for Pi's default.
|
|
1064
1064
|
if (workspace.defaultModel)
|
|
1065
1065
|
conversation.model = { provider: workspace.defaultModel.provider, modelId: workspace.defaultModel.modelId, label: workspace.defaultModel.label };
|
|
1066
1066
|
if (workspace.defaultThinkingLevel)
|
|
@@ -1072,6 +1072,8 @@ export class Store {
|
|
|
1072
1072
|
// rewritten: it describes how this conversation came to be, not what is true right now.
|
|
1073
1073
|
if (configuration.modelNotice !== undefined)
|
|
1074
1074
|
conversation.modelNotice = configuration.modelNotice;
|
|
1075
|
+
if (configuration.agentNotice !== undefined)
|
|
1076
|
+
conversation.agentNotice = configuration.agentNotice;
|
|
1075
1077
|
if (configuration.model !== undefined) {
|
|
1076
1078
|
if (configuration.model)
|
|
1077
1079
|
conversation.model = { ...configuration.model };
|
|
@@ -1084,8 +1086,12 @@ export class Store {
|
|
|
1084
1086
|
else
|
|
1085
1087
|
delete conversation.thinkingLevel;
|
|
1086
1088
|
}
|
|
1087
|
-
if (configuration.agentId)
|
|
1088
|
-
|
|
1089
|
+
if (configuration.agentId !== undefined) {
|
|
1090
|
+
if (configuration.agentId)
|
|
1091
|
+
conversation.agentId = configuration.agentId;
|
|
1092
|
+
else
|
|
1093
|
+
delete conversation.agentId;
|
|
1094
|
+
}
|
|
1089
1095
|
// THE SESSION TREE'S EDGE, WRITTEN AT BIRTH. Both fields describe how this conversation CAME TO
|
|
1090
1096
|
// BE, exactly like modelNotice above, so they are set once here and never rewritten: the tree
|
|
1091
1097
|
// the sidebar draws after a restart is the tree that was true when each session was opened.
|
|
@@ -1232,6 +1238,67 @@ export class Store {
|
|
|
1232
1238
|
delete conversation.archivedAt;
|
|
1233
1239
|
await this.persistMetadata(conversationId);
|
|
1234
1240
|
}
|
|
1241
|
+
/**
|
|
1242
|
+
* Archives one whole tree as one state write. Only a conversation with no existing parent may
|
|
1243
|
+
* start it; an archived parent is still a parent, so its active descendants cannot be archived
|
|
1244
|
+
* independently. All running checks happen before the first mutation, making rejection all-or-none.
|
|
1245
|
+
*/
|
|
1246
|
+
async archiveConversationTree(conversationId) {
|
|
1247
|
+
const root = this.requireConversation(conversationId);
|
|
1248
|
+
if (root.parentConversationId && this.state.conversations.some(item => item.id === root.parentConversationId)) {
|
|
1249
|
+
throw new Error("子会话不能独立归档,请从根会话归档整棵树");
|
|
1250
|
+
}
|
|
1251
|
+
if (root.archivedAt)
|
|
1252
|
+
return;
|
|
1253
|
+
const active = this.conversationSubtree(root).filter(item => !item.archivedAt);
|
|
1254
|
+
const running = active.find(item => item.status === "running");
|
|
1255
|
+
if (running)
|
|
1256
|
+
throw new Error(`会话「${running.title}」正在运行,无法归档整棵树,请先停止运行`);
|
|
1257
|
+
const archivedAt = now();
|
|
1258
|
+
for (const conversation of active)
|
|
1259
|
+
conversation.archivedAt = archivedAt;
|
|
1260
|
+
await this.persist();
|
|
1261
|
+
}
|
|
1262
|
+
/** Restores one archived tree in one write; active descendants are already restored by definition. */
|
|
1263
|
+
async unarchiveConversationTree(conversationId) {
|
|
1264
|
+
const root = this.requireConversation(conversationId);
|
|
1265
|
+
const parent = root.parentConversationId
|
|
1266
|
+
? this.state.conversations.find(item => item.id === root.parentConversationId)
|
|
1267
|
+
: undefined;
|
|
1268
|
+
if (parent?.archivedAt)
|
|
1269
|
+
throw new Error("归档树中的子会话不能独立恢复,请从归档树的根会话恢复整棵树");
|
|
1270
|
+
if (!root.archivedAt)
|
|
1271
|
+
return;
|
|
1272
|
+
const archived = this.conversationSubtree(root).filter(item => item.archivedAt);
|
|
1273
|
+
for (const conversation of archived)
|
|
1274
|
+
delete conversation.archivedAt;
|
|
1275
|
+
await this.persist();
|
|
1276
|
+
}
|
|
1277
|
+
/** Pre-order traversal with cycle protection; archived nodes are traversed, not treated as leaves. */
|
|
1278
|
+
conversationSubtree(root) {
|
|
1279
|
+
const byParent = new Map();
|
|
1280
|
+
for (const conversation of this.state.conversations) {
|
|
1281
|
+
if (conversation.workspaceId !== root.workspaceId || !conversation.parentConversationId)
|
|
1282
|
+
continue;
|
|
1283
|
+
const siblings = byParent.get(conversation.parentConversationId);
|
|
1284
|
+
if (siblings)
|
|
1285
|
+
siblings.push(conversation);
|
|
1286
|
+
else
|
|
1287
|
+
byParent.set(conversation.parentConversationId, [conversation]);
|
|
1288
|
+
}
|
|
1289
|
+
const result = [];
|
|
1290
|
+
const visited = new Set();
|
|
1291
|
+
const walk = (conversation) => {
|
|
1292
|
+
if (visited.has(conversation.id))
|
|
1293
|
+
return;
|
|
1294
|
+
visited.add(conversation.id);
|
|
1295
|
+
result.push(conversation);
|
|
1296
|
+
for (const child of byParent.get(conversation.id) ?? [])
|
|
1297
|
+
walk(child);
|
|
1298
|
+
};
|
|
1299
|
+
walk(root);
|
|
1300
|
+
return result;
|
|
1301
|
+
}
|
|
1235
1302
|
/**
|
|
1236
1303
|
* Rename edits the title and NOTHING else. In particular `updatedAt` is deliberately not touched,
|
|
1237
1304
|
* for exactly the reason archive is: `updatedAt` is the sidebar's recency key, so bumping it would
|
package/dist/server/types.d.ts
CHANGED
|
@@ -503,6 +503,9 @@ export type Conversation = {
|
|
|
503
503
|
* not what is true right now.
|
|
504
504
|
*/
|
|
505
505
|
modelNotice?: string;
|
|
506
|
+
/** Why this conversation started on Pi's default agent instead of the workspace's deleted default.
|
|
507
|
+
* Written once at birth, like modelNotice, and rendered verbatim. */
|
|
508
|
+
agentNotice?: string;
|
|
506
509
|
/** Absent means "Pi's own default", which is why a conversation created before the picker existed still works. */
|
|
507
510
|
model?: ModelSelection;
|
|
508
511
|
thinkingLevel?: string;
|