@evandu/super-pi 0.1.145 → 0.1.147
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/core.ts +74 -2
- package/agents/super-pi-task/index.ts +7 -0
- package/dist/.super-pi-check-inputs.json +2 -2
- package/dist/client/.super-pi-client-sources.json +11 -11
- package/dist/client/assets/{CodeMirrorEditor-A9KvbIkm.js → CodeMirrorEditor-DQCjEnxG.js} +2 -2
- package/dist/client/assets/{dist-DM4Olwfx.js → dist-1Innu17E.js} +1 -1
- package/dist/client/assets/{dist-bIDce5j-.js → dist-1lynFOc5.js} +1 -1
- package/dist/client/assets/{dist-BlxwDnWr.js → dist-B1qLZVMK.js} +1 -1
- package/dist/client/assets/{dist-BTVbZigv.js → dist-B4pz_xdd.js} +1 -1
- package/dist/client/assets/{dist-UJ46aPS3.js → dist-B6KwDpeI.js} +1 -1
- package/dist/client/assets/{dist-CK2KFFtT.js → dist-BJVI1Unv.js} +1 -1
- package/dist/client/assets/{dist-CRnm1rnO.js → dist-BWvzD8Pe.js} +1 -1
- package/dist/client/assets/{dist-DSHM-PAs.js → dist-BayWssd0.js} +1 -1
- package/dist/client/assets/{dist-B9DE7sc5.js → dist-Bk_pkBVH.js} +1 -1
- package/dist/client/assets/{dist-D-o7iQ8c.js → dist-BpeClAv0.js} +1 -1
- package/dist/client/assets/{dist-CQFASxyb.js → dist-BsmJg5PX.js} +1 -1
- package/dist/client/assets/{dist-Ds5gfeAi.js → dist-Bx7s4rni.js} +1 -1
- package/dist/client/assets/{dist-UPJPMjWv.js → dist-CDBKM9tL.js} +1 -1
- package/dist/client/assets/{dist-CIgjifH-.js → dist-CGzXCzCG.js} +1 -1
- package/dist/client/assets/{dist-BAiV8I_O.js → dist-CRxCwn-Z.js} +1 -1
- package/dist/client/assets/{dist-Br-AuQUG.js → dist-CTDgUnBG.js} +1 -1
- package/dist/client/assets/{dist-4JbFTwg2.js → dist-D1VfMWmf.js} +1 -1
- package/dist/client/assets/{dist-BYOpvW02.js → dist-DTZLNEci.js} +1 -1
- package/dist/client/assets/{dist-CQV5kOCE.js → dist-DuUi29km.js} +1 -1
- package/dist/client/assets/{dist-CjszAoXk.js → dist-F3S0t1oM.js} +1 -1
- package/dist/client/assets/{dist-2boAf_pN.js → dist-QZ-FAVJV.js} +1 -1
- package/dist/client/assets/{dist-B1p4YF9C.js → dist-u0nBg0fZ.js} +1 -1
- package/dist/client/assets/index-BMpqviwU.css +1 -0
- package/dist/client/assets/index-Dzyp_7sf.js +102 -0
- package/dist/client/index.html +2 -2
- package/dist/server/active-memory.d.ts +17 -0
- package/dist/server/active-memory.js +39 -0
- package/dist/server/agents.d.ts +1 -1
- package/dist/server/agents.js +3 -1
- package/dist/server/application/conversation-service.d.ts +1 -1
- package/dist/server/application/conversation-service.js +12 -4
- package/dist/server/application/run-service.d.ts +32 -2
- package/dist/server/application/run-service.js +296 -40
- package/dist/server/application/scheduled-task-service.d.ts +14 -1
- package/dist/server/application/scheduled-task-service.js +27 -2
- package/dist/server/application/session-orchestrator-service.d.ts +114 -3
- package/dist/server/application/session-orchestrator-service.js +406 -35
- package/dist/server/application/session-watchdog.d.ts +159 -0
- package/dist/server/application/session-watchdog.js +244 -0
- package/dist/server/command-handler.js +1 -1
- package/dist/server/conversation-usage.d.ts +19 -0
- package/dist/server/conversation-usage.js +40 -0
- package/dist/server/file-diff.d.ts +3 -0
- package/dist/server/file-diff.js +4 -4
- package/dist/server/file-index.d.ts +4 -2
- package/dist/server/file-index.js +14 -9
- package/dist/server/file-name-index.d.ts +5 -2
- package/dist/server/file-name-index.js +22 -5
- package/dist/server/git.d.ts +3 -3
- package/dist/server/git.js +10 -9
- package/dist/server/http-server.js +256 -34
- package/dist/server/index.js +3 -0
- package/dist/server/knowledge-vectors.d.ts +15 -0
- package/dist/server/knowledge-vectors.js +118 -37
- package/dist/server/knowledge.d.ts +7 -8
- package/dist/server/knowledge.js +133 -55
- package/dist/server/length-continuation.d.ts +50 -0
- package/dist/server/length-continuation.js +55 -0
- package/dist/server/model-context-window.d.ts +46 -0
- package/dist/server/model-context-window.js +121 -0
- package/dist/server/pi-session-events.js +5 -1
- package/dist/server/pi-session-pool.d.ts +2 -0
- package/dist/server/pi-session-pool.js +16 -0
- package/dist/server/pi-session.d.ts +6 -0
- package/dist/server/pi-session.js +15 -1
- package/dist/server/runtime.d.ts +3 -0
- package/dist/server/runtime.js +23 -7
- package/dist/server/session-cwd.d.ts +17 -0
- package/dist/server/session-cwd.js +43 -0
- package/dist/server/session-ledger.d.ts +7 -0
- package/dist/server/session-tree-scope.d.ts +125 -0
- package/dist/server/session-tree-scope.js +337 -0
- package/dist/server/store.d.ts +35 -0
- package/dist/server/store.js +87 -2
- package/dist/server/task-tool.d.ts +2 -1
- package/dist/server/task-tool.js +4 -1
- package/dist/server/terminal.d.ts +1 -0
- package/dist/server/terminal.js +5 -3
- package/dist/server/turn-failure.d.ts +26 -0
- package/dist/server/turn-failure.js +67 -1
- package/dist/server/types.d.ts +33 -1
- package/dist/server/version.d.ts +25 -2
- package/dist/server/version.js +98 -26
- package/dist/server/ws-server.d.ts +1 -1
- package/dist/server/ws-server.js +7 -5
- package/package.json +36 -3
- package/dist/client/assets/index-BGsP8pt_.js +0 -100
- package/dist/client/assets/index-CiIc734l.css +0 -1
|
@@ -19,4 +19,30 @@
|
|
|
19
19
|
* needs no server, socket or Pi process.
|
|
20
20
|
*/
|
|
21
21
|
export declare function formatTurnFailure(error: unknown): string;
|
|
22
|
+
/**
|
|
23
|
+
* THE MODEL FILLED ITS OUTPUT BUDGET AND STOPPED MID-ANSWER.
|
|
24
|
+
*
|
|
25
|
+
* A length stop is NOT a crash. The provider answered, streamed real text, and stopped because it
|
|
26
|
+
* ran out of `max_tokens` - which is what happens every time an answer is longer than one output
|
|
27
|
+
* budget. Treating it as a failure is what made an orchestrated child session die: the run service
|
|
28
|
+
* stamped the conversation `failed`, the orchestrator reported a failure to the parent, and a task
|
|
29
|
+
* that was one sentence from done was thrown away.
|
|
30
|
+
*
|
|
31
|
+
* It carries a CLASS so the layers above can tell it apart from a provider error by identity rather
|
|
32
|
+
* than by matching a Chinese substring - the message is written for a reader, and a reader-facing
|
|
33
|
+
* sentence is not a reliable machine contract.
|
|
34
|
+
*/
|
|
35
|
+
export declare class LengthExceededError extends Error {
|
|
36
|
+
readonly stopReason: "length";
|
|
37
|
+
constructor(message?: string);
|
|
38
|
+
}
|
|
39
|
+
/**
|
|
40
|
+
* Whether this failure is a length stop rather than a real failure.
|
|
41
|
+
*
|
|
42
|
+
* The class is the answer; the message match is the BACKSTOP for a throw site that never got to
|
|
43
|
+
* construct the class - an error that crossed a process boundary, or one that was rebuilt from its
|
|
44
|
+
* text somewhere along the way. Both are checked because a length stop that loses its class on the
|
|
45
|
+
* way up is exactly the case that used to kill a child session.
|
|
46
|
+
*/
|
|
47
|
+
export declare function isLengthExceededError(error: unknown): boolean;
|
|
22
48
|
export declare function controllerAborted(error: unknown): boolean;
|
|
@@ -43,7 +43,9 @@ export function formatTurnFailure(error) {
|
|
|
43
43
|
// WORKSPACE PATH - turn refused on directory the contract forbids. Refusal happens before
|
|
44
44
|
// anything is on screen, so the banner is also the only place the reason lives.
|
|
45
45
|
if (lower.includes("workspace directory") || lower.includes("does not exist or is not a directory")) {
|
|
46
|
-
|
|
46
|
+
const match = raw.match(/does not exist or is not a directory:\s*(.+?)(?:\.\s*Refusing|$)/);
|
|
47
|
+
const path = match?.[1]?.trim();
|
|
48
|
+
return `工作区目录不存在:${path || "未知路径"}`;
|
|
47
49
|
}
|
|
48
50
|
// PROVIDER UNREACHABLE - the request never reached the model. This is the branch a custom
|
|
49
51
|
// provider with a wrong Base URL lands in, and the person editing that URL needs to be told that
|
|
@@ -63,15 +65,79 @@ export function formatTurnFailure(error) {
|
|
|
63
65
|
if (code >= 500 && code <= 599 || /internal server error|bad gateway|service unavailable|gateway timeout/.test(lower)) {
|
|
64
66
|
return `模型供应商服务端出错了${code ? `(HTTP ${code})` : ""},通常稍后重试即可。`;
|
|
65
67
|
}
|
|
68
|
+
// CONTEXT OVERFLOW - the conversation outgrew the model's window.
|
|
69
|
+
//
|
|
70
|
+
// This is the branch a reader hits when the session stops working entirely, so it has to name the
|
|
71
|
+
// cause AND the way out. The upstream wording varies by provider, and the shapes that show up are
|
|
72
|
+
// "context length exceeded", "maximum context length", "too many tokens", "prompt is too long",
|
|
73
|
+
// "reduce the length of the messages", and the 400s some providers use for it.
|
|
74
|
+
//
|
|
75
|
+
// The action is the same in every case: the conversation is too long for one request, and the
|
|
76
|
+
// reader's choices are to start a new conversation or use a model with a bigger window. Saying
|
|
77
|
+
// "the model refused" would hide both.
|
|
78
|
+
if (/context[\s_]*(?:length|window|size)[\s_]*(?:exceeded|exceeds|too large|limit)|maximum context|context limit|too many tokens|prompt is too long|reduce the length of the messages|exceeds the maximum|上下文(?:超|过)限|上下文长度/.test(lower)) {
|
|
79
|
+
return "这一轮的内容超过了模型的上下文上限,模型没能读下整段对话。可以新开一个会话继续,或换一个上下文窗口更大的模型(Pi 会在超限前自动压缩,这轮没能完成压缩)。";
|
|
80
|
+
}
|
|
66
81
|
// IMAGE MISMATCH - explicit by the caller, no need to translate.
|
|
67
82
|
if (lower.includes("不支持图片输入")) {
|
|
68
83
|
return raw;
|
|
69
84
|
}
|
|
85
|
+
// LENGTH LIMIT - the model stopped because it filled the output budget, NOT because anything
|
|
86
|
+
// broke. The answer is incomplete, but everything that was said is still on screen, so the
|
|
87
|
+
// sentence has to offer the way out (say "continue") instead of calling it a failure.
|
|
88
|
+
//
|
|
89
|
+
// THE SENTENCE IS BUILT, NOT TAKEN FROM THE ERROR. The error's own message says the fact
|
|
90
|
+
// ("模型输出达到本轮长度上限,回答未完成") and stops there - which is a statement, not an action, and
|
|
91
|
+
// a reader who is told a thing stopped has no idea what to do next. What they need is the way
|
|
92
|
+
// out, so the sentence always carries it. Pi's own words are kept only when they add something
|
|
93
|
+
// the sentence does not already say.
|
|
94
|
+
if (isLengthExceededError(error)) {
|
|
95
|
+
const detail = raw && !/长度上限/.test(raw) ? `(${raw})` : "";
|
|
96
|
+
return `模型单次输出达到长度上限,回答没有写完${detail}。可以点「继续写完」或者自己发一句「继续」,模型会从被截断的地方接着写。`;
|
|
97
|
+
}
|
|
70
98
|
// GENERIC - the literal message is more useful than a paraphrase, but it has to be the LAST
|
|
71
99
|
// branch and the message has to be non-empty (a thrown `new Error()` is empty and renders
|
|
72
100
|
// nothing, which is exactly the silence this whole change exists to break).
|
|
73
101
|
return raw || "Pi 未能完成本轮回答,已标记为失败。可以重发任务继续。";
|
|
74
102
|
}
|
|
103
|
+
/**
|
|
104
|
+
* THE MODEL FILLED ITS OUTPUT BUDGET AND STOPPED MID-ANSWER.
|
|
105
|
+
*
|
|
106
|
+
* A length stop is NOT a crash. The provider answered, streamed real text, and stopped because it
|
|
107
|
+
* ran out of `max_tokens` - which is what happens every time an answer is longer than one output
|
|
108
|
+
* budget. Treating it as a failure is what made an orchestrated child session die: the run service
|
|
109
|
+
* stamped the conversation `failed`, the orchestrator reported a failure to the parent, and a task
|
|
110
|
+
* that was one sentence from done was thrown away.
|
|
111
|
+
*
|
|
112
|
+
* It carries a CLASS so the layers above can tell it apart from a provider error by identity rather
|
|
113
|
+
* than by matching a Chinese substring - the message is written for a reader, and a reader-facing
|
|
114
|
+
* sentence is not a reliable machine contract.
|
|
115
|
+
*/
|
|
116
|
+
export class LengthExceededError extends Error {
|
|
117
|
+
stopReason = "length";
|
|
118
|
+
constructor(message) {
|
|
119
|
+
super(message || "模型输出达到本轮长度上限,回答未完成。");
|
|
120
|
+
this.name = "LengthExceededError";
|
|
121
|
+
}
|
|
122
|
+
}
|
|
123
|
+
/**
|
|
124
|
+
* Whether this failure is a length stop rather than a real failure.
|
|
125
|
+
*
|
|
126
|
+
* The class is the answer; the message match is the BACKSTOP for a throw site that never got to
|
|
127
|
+
* construct the class - an error that crossed a process boundary, or one that was rebuilt from its
|
|
128
|
+
* text somewhere along the way. Both are checked because a length stop that loses its class on the
|
|
129
|
+
* way up is exactly the case that used to kill a child session.
|
|
130
|
+
*/
|
|
131
|
+
export function isLengthExceededError(error) {
|
|
132
|
+
if (!error)
|
|
133
|
+
return false;
|
|
134
|
+
if (error instanceof LengthExceededError)
|
|
135
|
+
return true;
|
|
136
|
+
if (error?.stopReason === "length")
|
|
137
|
+
return true;
|
|
138
|
+
const msg = error instanceof Error ? error.message : typeof error === "string" ? error : "";
|
|
139
|
+
return msg.includes("模型输出达到本轮长度上限") || msg.includes("达到本轮长度上限") || msg.includes("单次输出达到长度上限");
|
|
140
|
+
}
|
|
75
141
|
export function controllerAborted(error) {
|
|
76
142
|
if (!error)
|
|
77
143
|
return false;
|
package/dist/server/types.d.ts
CHANGED
|
@@ -18,7 +18,7 @@ import type { ScheduledTask } from "./scheduled-tasks.js";
|
|
|
18
18
|
* `failed` is what any unhandled throw lands (with `Conversation.error` carrying the translated reason);
|
|
19
19
|
* `completed` is the clean path.
|
|
20
20
|
*/
|
|
21
|
-
export type RunStatus = "idle" | "waiting" | "running" | "completed" | "stopped" | "failed";
|
|
21
|
+
export type RunStatus = "idle" | "waiting" | "running" | "awaiting_background" | "completed" | "stopped" | "failed";
|
|
22
22
|
export type MessageRole = "user" | "assistant" | "tool" | "system";
|
|
23
23
|
/**
|
|
24
24
|
* One message in `state.json`.
|
|
@@ -464,6 +464,12 @@ export type Conversation = {
|
|
|
464
464
|
status: RunStatus;
|
|
465
465
|
messages: Message[];
|
|
466
466
|
archivedAt?: string;
|
|
467
|
+
/** Cumulative primary-assistant usage observed since readout collection began; excludes subagents. */
|
|
468
|
+
usage?: SubagentUsage;
|
|
469
|
+
/** False means cost is only a sum of reported costs, not a complete price (zero is not "free"). */
|
|
470
|
+
usageCostKnown?: boolean;
|
|
471
|
+
contextUsage?: ConversationContextUsage;
|
|
472
|
+
usageUpdatedAt?: string;
|
|
467
473
|
/** Transport projection only: false means messages were omitted, rather than an empty transcript. */
|
|
468
474
|
messagesLoaded?: boolean;
|
|
469
475
|
/** Pending question summary, retained when a focused snapshot omits this conversation's messages. */
|
|
@@ -488,6 +494,20 @@ export type Conversation = {
|
|
|
488
494
|
* would answer "what happened" with something the reader has to go and decode.
|
|
489
495
|
*/
|
|
490
496
|
error?: string;
|
|
497
|
+
/**
|
|
498
|
+
* WHY THIS CONVERSATION'S LAST ANSWER IS INCOMPLETE - in words a person reads.
|
|
499
|
+
*
|
|
500
|
+
* A WARNING, not a failure. The model filled its single-response output budget and stopped
|
|
501
|
+
* mid-answer; everything it said is on screen, and the conversation is still usable. It is a
|
|
502
|
+
* separate field from `error` because the two mean different things to the reader: `error` says
|
|
503
|
+
* "this run broke, here is why", this says "this answer was cut short, and here is what to do
|
|
504
|
+
* about it". Collapsing them would paint a recoverable truncation as a crash - which is exactly
|
|
505
|
+
* what made an orchestrated child session die.
|
|
506
|
+
*
|
|
507
|
+
* Cleared by the next turn, the same way `error` is: it describes the turn that just ended and
|
|
508
|
+
* must never outlive it.
|
|
509
|
+
*/
|
|
510
|
+
warning?: string;
|
|
491
511
|
/**
|
|
492
512
|
* WHY THIS CONVERSATION IS NOT ON THE MODEL THE USER SET FOR IT - in words a person reads.
|
|
493
513
|
*
|
|
@@ -519,6 +539,8 @@ export type Conversation = {
|
|
|
519
539
|
* deleted agent degrades to "no role" instead of leaving a stale snapshot in state.json.
|
|
520
540
|
*/
|
|
521
541
|
agentId?: string;
|
|
542
|
+
/** Relative to the workspace root. Absent or empty preserves the original workspace-root cwd. */
|
|
543
|
+
cwd?: string;
|
|
522
544
|
/**
|
|
523
545
|
* The conversation that opened this one, when an orchestrator agent created it. Absent for a
|
|
524
546
|
* conversation the user started. This is the session tree's edge, and it PERSISTS: the sidebar
|
|
@@ -655,6 +677,12 @@ export type SubagentUsage = {
|
|
|
655
677
|
cost: number;
|
|
656
678
|
turns: number;
|
|
657
679
|
};
|
|
680
|
+
/** Current context occupancy, distinct from cumulative billed tokens; null means unknown after compaction. */
|
|
681
|
+
export type ConversationContextUsage = {
|
|
682
|
+
tokens: number | null;
|
|
683
|
+
contextWindow: number;
|
|
684
|
+
percent: number | null;
|
|
685
|
+
};
|
|
658
686
|
/**
|
|
659
687
|
* ONE TOOL CALL, as the card shows it, and the boundary that keeps a child's turn out of `state.json`.
|
|
660
688
|
*
|
|
@@ -1022,6 +1050,7 @@ export type ClientCommandBody =
|
|
|
1022
1050
|
type: "terminal.open";
|
|
1023
1051
|
sessionId: string;
|
|
1024
1052
|
workspaceId: string;
|
|
1053
|
+
cwd?: string;
|
|
1025
1054
|
cols?: number;
|
|
1026
1055
|
rows?: number;
|
|
1027
1056
|
}
|
|
@@ -1182,6 +1211,7 @@ export type ClientCommandBody =
|
|
|
1182
1211
|
} | {
|
|
1183
1212
|
type: "conversation.create";
|
|
1184
1213
|
workspaceId: string;
|
|
1214
|
+
cwd?: string;
|
|
1185
1215
|
} | {
|
|
1186
1216
|
type: "conversation.select";
|
|
1187
1217
|
conversationId: string;
|
|
@@ -1284,6 +1314,7 @@ export type ClientCommandBody =
|
|
|
1284
1314
|
| {
|
|
1285
1315
|
type: "files.index.subscribe";
|
|
1286
1316
|
workspaceId?: string;
|
|
1317
|
+
cwd?: string;
|
|
1287
1318
|
} | {
|
|
1288
1319
|
type: "state.subscribe";
|
|
1289
1320
|
conversationId?: string;
|
|
@@ -1601,6 +1632,7 @@ export type ServerEvent = {
|
|
|
1601
1632
|
} | {
|
|
1602
1633
|
type: "files.index";
|
|
1603
1634
|
workspaceId: string;
|
|
1635
|
+
cwd?: string;
|
|
1604
1636
|
version: number;
|
|
1605
1637
|
changed: {
|
|
1606
1638
|
path: string;
|
package/dist/server/version.d.ts
CHANGED
|
@@ -1,10 +1,33 @@
|
|
|
1
1
|
import type { ServerVersionInfo } from "./types.js";
|
|
2
|
+
export declare const UNKNOWN_VERSION = "0.0.0-unknown";
|
|
3
|
+
/**
|
|
4
|
+
* Reads and validates the version string from a package.json located in the given directory.
|
|
5
|
+
*/
|
|
6
|
+
export declare function readPackageVersion(dir?: string): string | undefined;
|
|
7
|
+
/**
|
|
8
|
+
* Resolves the version of Super Pi itself by searching relative to this module's location.
|
|
9
|
+
* In both development (`src/server/`) and production build (`dist/server/`), two levels up
|
|
10
|
+
* is the repository / package root containing package.json.
|
|
11
|
+
*/
|
|
12
|
+
export declare function resolveSelfVersion(fromDir?: string): string | undefined;
|
|
2
13
|
/**
|
|
3
14
|
* Initializes the version captured when this server process booted up.
|
|
15
|
+
* If the provided rootDir fails to yield a valid package.json version,
|
|
16
|
+
* attempts to self-heal using the module's own location before falling back to UNKNOWN_VERSION.
|
|
17
|
+
*/
|
|
18
|
+
export declare function initRunningVersion(rootDir: string, selfFromDir?: string): string;
|
|
19
|
+
/**
|
|
20
|
+
* Returns the currently running version captured in memory.
|
|
21
|
+
*/
|
|
22
|
+
export declare function getRunningVersion(): string;
|
|
23
|
+
/**
|
|
24
|
+
* Resets the in-memory running version, primarily for testing purposes.
|
|
4
25
|
*/
|
|
5
|
-
export declare function
|
|
26
|
+
export declare function resetRunningVersion(toVersion?: string): string;
|
|
6
27
|
/**
|
|
7
28
|
* Checks whether the package.json version on disk has been updated beyond
|
|
8
29
|
* the version currently executing in this process.
|
|
30
|
+
* If rootDir fails to yield a valid package.json version, attempts to self-heal
|
|
31
|
+
* using the module's own location.
|
|
9
32
|
*/
|
|
10
|
-
export declare function checkDiskVersion(rootDir: string): ServerVersionInfo;
|
|
33
|
+
export declare function checkDiskVersion(rootDir: string, selfFromDir?: string): ServerVersionInfo;
|
package/dist/server/version.js
CHANGED
|
@@ -1,39 +1,111 @@
|
|
|
1
|
-
import { readFileSync } from "node:fs";
|
|
2
|
-
import { join } from "node:path";
|
|
3
|
-
|
|
1
|
+
import { existsSync, readFileSync } from "node:fs";
|
|
2
|
+
import { dirname, join, resolve } from "node:path";
|
|
3
|
+
export const UNKNOWN_VERSION = "0.0.0-unknown";
|
|
4
4
|
/**
|
|
5
|
-
*
|
|
5
|
+
* Reads and validates the version string from a package.json located in the given directory.
|
|
6
6
|
*/
|
|
7
|
-
export function
|
|
7
|
+
export function readPackageVersion(dir) {
|
|
8
|
+
if (!dir || typeof dir !== "string")
|
|
9
|
+
return undefined;
|
|
8
10
|
try {
|
|
9
|
-
const
|
|
10
|
-
if (
|
|
11
|
-
|
|
11
|
+
const pkgPath = join(dir, "package.json");
|
|
12
|
+
if (!existsSync(pkgPath))
|
|
13
|
+
return undefined;
|
|
14
|
+
const raw = readFileSync(pkgPath, "utf8");
|
|
15
|
+
const pkg = JSON.parse(raw);
|
|
16
|
+
if (pkg && typeof pkg.version === "string" && pkg.version.trim().length > 0) {
|
|
17
|
+
return pkg.version.trim();
|
|
18
|
+
}
|
|
12
19
|
}
|
|
13
20
|
catch {
|
|
14
|
-
//
|
|
21
|
+
// Read or JSON parse error - return undefined to allow self-healing / fallback
|
|
22
|
+
}
|
|
23
|
+
return undefined;
|
|
24
|
+
}
|
|
25
|
+
/**
|
|
26
|
+
* Resolves the version of Super Pi itself by searching relative to this module's location.
|
|
27
|
+
* In both development (`src/server/`) and production build (`dist/server/`), two levels up
|
|
28
|
+
* is the repository / package root containing package.json.
|
|
29
|
+
*/
|
|
30
|
+
export function resolveSelfVersion(fromDir = import.meta.dirname) {
|
|
31
|
+
if (!fromDir || typeof fromDir !== "string")
|
|
32
|
+
return undefined;
|
|
33
|
+
// Primary path: two levels up from server module directory (src/server or dist/server)
|
|
34
|
+
const canonicalRoot = resolve(fromDir, "../..");
|
|
35
|
+
const canonicalVersion = readPackageVersion(canonicalRoot);
|
|
36
|
+
if (canonicalVersion)
|
|
37
|
+
return canonicalVersion;
|
|
38
|
+
// Secondary path: walk upwards to locate the nearest directory containing Super Pi's package.json
|
|
39
|
+
let current = resolve(fromDir);
|
|
40
|
+
while (true) {
|
|
41
|
+
try {
|
|
42
|
+
const pkgPath = join(current, "package.json");
|
|
43
|
+
if (existsSync(pkgPath)) {
|
|
44
|
+
const pkg = JSON.parse(readFileSync(pkgPath, "utf8"));
|
|
45
|
+
if (pkg && pkg.name === "@evandu/super-pi" && typeof pkg.version === "string" && pkg.version.trim()) {
|
|
46
|
+
return pkg.version.trim();
|
|
47
|
+
}
|
|
48
|
+
}
|
|
49
|
+
}
|
|
50
|
+
catch {
|
|
51
|
+
// Continue searching upwards
|
|
52
|
+
}
|
|
53
|
+
const parent = dirname(current);
|
|
54
|
+
if (parent === current)
|
|
55
|
+
break;
|
|
56
|
+
current = parent;
|
|
57
|
+
}
|
|
58
|
+
return undefined;
|
|
59
|
+
}
|
|
60
|
+
let runningVersion = resolveSelfVersion() ?? UNKNOWN_VERSION;
|
|
61
|
+
/**
|
|
62
|
+
* Initializes the version captured when this server process booted up.
|
|
63
|
+
* If the provided rootDir fails to yield a valid package.json version,
|
|
64
|
+
* attempts to self-heal using the module's own location before falling back to UNKNOWN_VERSION.
|
|
65
|
+
*/
|
|
66
|
+
export function initRunningVersion(rootDir, selfFromDir = import.meta.dirname) {
|
|
67
|
+
const direct = readPackageVersion(rootDir);
|
|
68
|
+
if (direct) {
|
|
69
|
+
runningVersion = direct;
|
|
70
|
+
return runningVersion;
|
|
71
|
+
}
|
|
72
|
+
const self = resolveSelfVersion(selfFromDir);
|
|
73
|
+
if (self) {
|
|
74
|
+
runningVersion = self;
|
|
75
|
+
return runningVersion;
|
|
15
76
|
}
|
|
77
|
+
runningVersion = UNKNOWN_VERSION;
|
|
78
|
+
return runningVersion;
|
|
79
|
+
}
|
|
80
|
+
/**
|
|
81
|
+
* Returns the currently running version captured in memory.
|
|
82
|
+
*/
|
|
83
|
+
export function getRunningVersion() {
|
|
84
|
+
return runningVersion;
|
|
85
|
+
}
|
|
86
|
+
/**
|
|
87
|
+
* Resets the in-memory running version, primarily for testing purposes.
|
|
88
|
+
*/
|
|
89
|
+
export function resetRunningVersion(toVersion) {
|
|
90
|
+
runningVersion = toVersion ?? resolveSelfVersion() ?? UNKNOWN_VERSION;
|
|
16
91
|
return runningVersion;
|
|
17
92
|
}
|
|
18
93
|
/**
|
|
19
94
|
* Checks whether the package.json version on disk has been updated beyond
|
|
20
95
|
* the version currently executing in this process.
|
|
96
|
+
* If rootDir fails to yield a valid package.json version, attempts to self-heal
|
|
97
|
+
* using the module's own location.
|
|
21
98
|
*/
|
|
22
|
-
export function checkDiskVersion(rootDir) {
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
runningVersion,
|
|
35
|
-
diskVersion: runningVersion,
|
|
36
|
-
needsRestart: false,
|
|
37
|
-
};
|
|
38
|
-
}
|
|
99
|
+
export function checkDiskVersion(rootDir, selfFromDir = import.meta.dirname) {
|
|
100
|
+
const diskVersion = readPackageVersion(rootDir) ??
|
|
101
|
+
resolveSelfVersion(selfFromDir) ??
|
|
102
|
+
runningVersion;
|
|
103
|
+
const needsRestart = Boolean(diskVersion !== runningVersion &&
|
|
104
|
+
runningVersion !== UNKNOWN_VERSION &&
|
|
105
|
+
diskVersion !== UNKNOWN_VERSION);
|
|
106
|
+
return {
|
|
107
|
+
runningVersion,
|
|
108
|
+
diskVersion,
|
|
109
|
+
needsRestart,
|
|
110
|
+
};
|
|
39
111
|
}
|
|
@@ -71,7 +71,7 @@ export declare class WsBroadcaster implements EventBroadcaster {
|
|
|
71
71
|
unregister(socket: WebSocket): void;
|
|
72
72
|
subscribe(socket: WebSocket, conversationId?: string): void;
|
|
73
73
|
sendState(socket: WebSocket): void;
|
|
74
|
-
subscribeFileIndex(socket: WebSocket, workspaceId?: string): void;
|
|
74
|
+
subscribeFileIndex(socket: WebSocket, workspaceId?: string, cwd?: string): void;
|
|
75
75
|
private receives;
|
|
76
76
|
configureStreaming(socket: WebSocket, coalesce: boolean): void;
|
|
77
77
|
private flushStreaming;
|
package/dist/server/ws-server.js
CHANGED
|
@@ -116,9 +116,9 @@ export class WsBroadcaster {
|
|
|
116
116
|
this.flushStreaming(socket);
|
|
117
117
|
safeSend(socket, JSON.stringify({ type: "state", state: this.getState(this.scopes.get(socket)) }));
|
|
118
118
|
}
|
|
119
|
-
subscribeFileIndex(socket, workspaceId) {
|
|
119
|
+
subscribeFileIndex(socket, workspaceId, cwd = "") {
|
|
120
120
|
if (workspaceId)
|
|
121
|
-
this.fileIndexScopes.set(socket, workspaceId);
|
|
121
|
+
this.fileIndexScopes.set(socket, { workspaceId, cwd });
|
|
122
122
|
else
|
|
123
123
|
this.fileIndexScopes.delete(socket);
|
|
124
124
|
}
|
|
@@ -127,8 +127,10 @@ export class WsBroadcaster {
|
|
|
127
127
|
if (!scope)
|
|
128
128
|
return true;
|
|
129
129
|
switch (event.type) {
|
|
130
|
-
case "files.index":
|
|
131
|
-
|
|
130
|
+
case "files.index": {
|
|
131
|
+
const fileScope = this.fileIndexScopes.get(socket);
|
|
132
|
+
return fileScope?.workspaceId === event.workspaceId && fileScope.cwd === (event.cwd ?? "");
|
|
133
|
+
}
|
|
132
134
|
case "message.delta":
|
|
133
135
|
case "message.thinking":
|
|
134
136
|
case "message.tools":
|
|
@@ -481,7 +483,7 @@ export class WsServer {
|
|
|
481
483
|
return;
|
|
482
484
|
}
|
|
483
485
|
if (parsed.type === "files.index.subscribe") {
|
|
484
|
-
this.broadcaster.subscribeFileIndex(socket, typeof parsed.workspaceId === "string" ? parsed.workspaceId : undefined);
|
|
486
|
+
this.broadcaster.subscribeFileIndex(socket, typeof parsed.workspaceId === "string" ? parsed.workspaceId : undefined, typeof parsed.cwd === "string" ? parsed.cwd : "");
|
|
485
487
|
return;
|
|
486
488
|
}
|
|
487
489
|
if (parsed.type === "stream.configure") {
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@evandu/super-pi",
|
|
3
|
-
"version": "0.1.
|
|
3
|
+
"version": "0.1.147",
|
|
4
4
|
"description": "Web control plane and companion for Pi",
|
|
5
5
|
"license": "MIT",
|
|
6
6
|
"type": "module",
|
|
@@ -51,6 +51,7 @@
|
|
|
51
51
|
"scripts/inline-thinking.test.mjs",
|
|
52
52
|
"scripts/inline-thinking-close-tag.test.mjs",
|
|
53
53
|
"scripts/pi-session.test.mjs",
|
|
54
|
+
"scripts/length-continuation.test.mjs",
|
|
54
55
|
"scripts/pi-session-isolation.test.mjs",
|
|
55
56
|
"scripts/installed-layout.test.mjs",
|
|
56
57
|
"scripts/pi-model-switch.test.mjs",
|
|
@@ -67,6 +68,7 @@
|
|
|
67
68
|
"scripts/verify-source-text.test.mjs",
|
|
68
69
|
"scripts/runtime-extension-ui.test.mjs",
|
|
69
70
|
"scripts/agent-mechanism.test.mjs",
|
|
71
|
+
"scripts/super-pi-tool-names.test.mjs",
|
|
70
72
|
"scripts/agent-wiring.test.mjs",
|
|
71
73
|
"scripts/message-timestamps.test.mjs",
|
|
72
74
|
"scripts/pi-resolution.test.mjs",
|
|
@@ -83,6 +85,8 @@
|
|
|
83
85
|
"scripts/session-tree-collapse.test.mjs",
|
|
84
86
|
"scripts/archive-cascade.test.mjs",
|
|
85
87
|
"scripts/workspace-cwd.test.mjs",
|
|
88
|
+
"scripts/session-cwd.test.mjs",
|
|
89
|
+
"scripts/session-cwd-panels.test.mjs",
|
|
86
90
|
"scripts/subagents.test.mjs",
|
|
87
91
|
"scripts/subagent-noise.test.mjs",
|
|
88
92
|
"scripts/subagent-response-receipt.test.mjs",
|
|
@@ -99,6 +103,7 @@
|
|
|
99
103
|
"scripts/queue-store.test.mjs",
|
|
100
104
|
"scripts/git-api.test.mjs",
|
|
101
105
|
"scripts/turn-failure-marks-failed.test.mjs",
|
|
106
|
+
"scripts/model-context-window.test.mjs",
|
|
102
107
|
"scripts/update-ui-contract.test.mjs",
|
|
103
108
|
"scripts/temp-sweep.test.mjs",
|
|
104
109
|
"scripts/workspace-identity.test.mjs",
|
|
@@ -188,11 +193,30 @@
|
|
|
188
193
|
"scripts/document-write-overwrite.test.mjs",
|
|
189
194
|
"scripts/feishu.test.mjs",
|
|
190
195
|
"scripts/scheduled-tasks.test.mjs",
|
|
196
|
+
"scripts/scheduled-tasks-timezone.test.mjs",
|
|
191
197
|
"scripts/scheduled-task-model-clear.test.mjs",
|
|
192
198
|
"scripts/scheduled-task-scope.test.mjs",
|
|
193
199
|
"scripts/scheduled-task-tool.test.mjs",
|
|
194
200
|
"scripts/run-state-sync.test.mjs",
|
|
195
|
-
"scripts/performance-architecture.test.mjs"
|
|
201
|
+
"scripts/performance-architecture.test.mjs",
|
|
202
|
+
"scripts/upgrade-from-138-browser.test.mjs",
|
|
203
|
+
"scripts/session-orchestrator-role-prompt.test.mjs",
|
|
204
|
+
"scripts/context-overflow-failure.test.mjs",
|
|
205
|
+
"scripts/spawn-workspace-boundary.test.mjs",
|
|
206
|
+
"scripts/cwd-agent-spawn.test.mjs",
|
|
207
|
+
"scripts/capabilities-prompt.test.mjs",
|
|
208
|
+
"scripts/version-fallback.test.mjs",
|
|
209
|
+
"scripts/model-image-input-session.test.mjs",
|
|
210
|
+
"scripts/conversation-usage.test.mjs",
|
|
211
|
+
"scripts/orchestrator-observability.test.mjs",
|
|
212
|
+
"scripts/session-tab-preview.test.mjs",
|
|
213
|
+
"scripts/knowledge-retrieval-quality.test.mjs",
|
|
214
|
+
"scripts/rail-ui-contract.test.mjs",
|
|
215
|
+
"scripts/session-tree-scope.test.mjs",
|
|
216
|
+
"scripts/session-spawn-model.test.mjs",
|
|
217
|
+
"scripts/background-result-steer.test.mjs",
|
|
218
|
+
"scripts/session-report-watchdog.test.mjs",
|
|
219
|
+
"scripts/awaiting-background-status.test.mjs"
|
|
196
220
|
],
|
|
197
221
|
"check:browser": [
|
|
198
222
|
"scripts/interrupt-browser.test.mjs",
|
|
@@ -231,6 +255,7 @@
|
|
|
231
255
|
"scripts/archive-restore-race-browser.test.mjs",
|
|
232
256
|
"scripts/tool-call-blocks-browser.test.mjs",
|
|
233
257
|
"scripts/model-notice-browser.test.mjs",
|
|
258
|
+
"scripts/length-warning-browser.test.mjs",
|
|
234
259
|
"scripts/dead-default-agent-browser.test.mjs",
|
|
235
260
|
"scripts/assistant-working-indicator-browser.test.mjs",
|
|
236
261
|
"scripts/thinking-first-delta-once-browser.test.mjs",
|
|
@@ -268,7 +293,15 @@
|
|
|
268
293
|
"scripts/ux-polish-browser.test.mjs",
|
|
269
294
|
"scripts/prompt-templates-browser.test.mjs",
|
|
270
295
|
"scripts/documents-browser.test.mjs",
|
|
271
|
-
"scripts/feishu-browser.test.mjs"
|
|
296
|
+
"scripts/feishu-browser.test.mjs",
|
|
297
|
+
"scripts/session-read-state-regression.test.mjs",
|
|
298
|
+
"scripts/orchestrated-order-role-browser.test.mjs",
|
|
299
|
+
"scripts/result-file-link-browser.test.mjs",
|
|
300
|
+
"scripts/git-panel-confirm-browser.test.mjs",
|
|
301
|
+
"scripts/session-origin-badge-browser.test.mjs",
|
|
302
|
+
"scripts/rail-ui-layout-browser.test.mjs",
|
|
303
|
+
"scripts/session-landing-browser.test.mjs",
|
|
304
|
+
"scripts/responsive-rail-drawer-browser.test.mjs"
|
|
272
305
|
],
|
|
273
306
|
"dependencies": {
|
|
274
307
|
"@codemirror/commands": "^6.11.1",
|