1+ import * as fs from "fs" ;
2+ import * as path from "path" ;
3+ import * as os from "os" ;
4+ import * as crypto from "crypto" ;
5+ import { fileURLToPath } from "url" ;
6+ import matter from "gray-matter" ;
7+ import ejs from "ejs" ;
8+ import type { ChatCompletionMessageParam , ChatCompletionContentPart } from "openai/resources/chat/completions" ;
9+ import { launchNotifyScript } from "./common/notify" ;
10+ import { buildThinkingRequestOptions } from "./common/openai-thinking" ;
11+ import { DEEPSEEK_V4_MODELS , supportsMultimodal } from "./common/model-capabilities" ;
12+ import {
13+ getCompactPrompt ,
14+ getDefaultSkillPrompt ,
15+ getRuntimeContext ,
16+ getSystemPrompt ,
17+ getTools ,
18+ type ToolDefinition ,
19+ } from "./prompt" ;
20+ import {
21+ ToolExecutor ,
22+ type CreateOpenAIClient ,
23+ type ProcessTimeoutControl ,
24+ type ProcessTimeoutInfo ,
25+ type ToolCallExecution ,
26+ type ToolExecutionHooks ,
27+ } from "./tools/executor" ;
28+ import { McpManager } from "./mcp/mcp-manager" ;
129import type { McpServerConfig , PermissionScope , PermissionSettings } from "./settings" ;
2- import type { AskPermissionRequest , MessageToolPermission , UserToolPermission } from "./common/permissions" ;
3- import type { CreateOpenAIClient } from "./tools/executor" ;
30+ import { logApiError } from "./common/error-logger" ;
31+ import { logOpenAIChatCompletionDebug , normalizeDebugError } from "./common/debug-logger" ;
32+ import { killProcessTree } from "./common/process-tree" ;
33+ import { GitFileHistory } from "./common/file-history" ;
34+ import { getSnippet } from "./common/state" ;
35+ import {
36+ appendProjectPermissionAllows ,
37+ buildPermissionToolExecution ,
38+ computeToolCallPermissions ,
39+ hasUserPermissionReplies ,
40+ normalizeAskPermissions ,
41+ parseToolCallForPermissions ,
42+ type AskPermissionRequest ,
43+ type MessageToolPermission ,
44+ type PermissionToolCall ,
45+ type UserToolPermission ,
46+ } from "./common/permissions" ;
47+
48+ export type { PermissionScope } from "./settings" ;
49+ export type {
50+ AskPermissionRequest ,
51+ AskPermissionScope ,
52+ BashPermissionScope ,
53+ MessageToolPermission ,
54+ PermissionDecision ,
55+ UserToolPermission ,
56+ } from "./common/permissions" ;
57+
58+ const MAX_SESSION_ENTRIES = 50 ;
59+ const DEFAULT_NEW_PROMPT_API_URL = "https://deepcode.vegamo.cn/api/plugin/new" ;
60+ const NEW_PROMPT_REPORT_TIMEOUT_MS = 3000 ;
61+ const DEFAULT_COMPACT_PROMPT_TOKEN_THRESHOLD = 128 * 1024 ;
62+ const DEEPSEEK_V4_COMPACT_PROMPT_TOKEN_THRESHOLD = 512 * 1024 ;
63+
64+ type ChatCompletionDebugOptions = {
65+ enabled ?: boolean ;
66+ location : string ;
67+ baseURL ?: string ;
68+ params ?: Record < string , unknown > ;
69+ } ;
70+
71+ export function getCompactPromptTokenThreshold ( model : string ) : number {
72+ return DEEPSEEK_V4_MODELS . has ( model )
73+ ? DEEPSEEK_V4_COMPACT_PROMPT_TOKEN_THRESHOLD
74+ : DEFAULT_COMPACT_PROMPT_TOKEN_THRESHOLD ;
75+ }
76+
77+ function isUsageRecord ( value : unknown ) : value is Record < string , unknown > {
78+ return value !== null && typeof value === "object" && ! Array . isArray ( value ) ;
79+ }
80+
81+ function summarizeCompletionOptions ( options ?: Record < string , unknown > ) : Record < string , unknown > | undefined {
82+ if ( ! options ) {
83+ return undefined ;
84+ }
85+ return {
86+ ...options ,
87+ signal : options . signal instanceof AbortSignal ? { aborted : options . signal . aborted } : options . signal ,
88+ } ;
89+ }
90+
91+ function addUsageValue ( current : unknown , next : unknown ) : unknown {
92+ if ( typeof next === "number" ) {
93+ return ( typeof current === "number" ? current : 0 ) + next ;
94+ }
95+
96+ if ( isUsageRecord ( next ) ) {
97+ const currentRecord = isUsageRecord ( current ) ? current : { } ;
98+ const result : Record < string , unknown > = { ...currentRecord } ;
99+ for ( const [ key , value ] of Object . entries ( next ) ) {
100+ result [ key ] = addUsageValue ( currentRecord [ key ] , value ) ;
101+ }
102+ return result ;
103+ }
104+
105+ return next ;
106+ }
107+
108+ function accumulateUsage ( current : ModelUsage | null , next : unknown | null | undefined ) : ModelUsage | null {
109+ if ( next == null ) {
110+ return current ?? null ;
111+ }
112+ return addUsageValue ( current , next ) as ModelUsage ;
113+ }
114+
115+ function usageWithRequestCount ( usage : ModelUsage ) : ModelUsage {
116+ const totalReqs = typeof usage . total_reqs === "number" ? usage . total_reqs + 1 : 1 ;
117+ return {
118+ ...usage ,
119+ total_reqs : totalReqs ,
120+ } ;
121+ }
122+
123+ function accumulateUsagePerModel (
124+ current : Record < string , ModelUsage > | null | undefined ,
125+ model : string ,
126+ next : ModelUsage | null | undefined
127+ ) : Record < string , ModelUsage > | null {
128+ if ( next == null ) {
129+ return current ?? null ;
130+ }
131+
132+ const usagePerModel = { ...( current ?? { } ) } ;
133+ const modelName = model . trim ( ) || "unknown" ;
134+ usagePerModel [ modelName ] = accumulateUsage ( usagePerModel [ modelName ] ?? null , usageWithRequestCount ( next ) ) ! ;
135+ return usagePerModel ;
136+ }
137+
138+ function getExtensionRoot ( ) : string {
139+ if ( typeof __dirname !== "undefined" ) {
140+ return path . resolve ( __dirname , ".." ) ;
141+ }
142+
143+ const currentFilePath = fileURLToPath ( import . meta. url ) ;
144+ return path . resolve ( path . dirname ( currentFilePath ) , ".." ) ;
145+ }
146+
147+ function getTotalTokens ( usage : ModelUsage | null | undefined ) : number {
148+ if ( ! isUsageRecord ( usage ) ) {
149+ return 0 ;
150+ }
151+ const totalTokens = usage . total_tokens ;
152+ return typeof totalTokens === "number" ? totalTokens : 0 ;
153+ }
4154
5155export type SessionStatus =
6156 | "failed"
@@ -52,7 +202,7 @@ export type SessionEntry = {
52202 activeTokens : number ;
53203 createTime : string ;
54204 updateTime : string ;
55- processes : Map < string , SessionProcessEntry > | null ;
205+ processes : Map < string , SessionProcessEntry > | null ; // {pid: process info}
56206 askPermissions ?: AskPermissionRequest [ ] ;
57207} ;
58208
@@ -113,7 +263,7 @@ export type SkillInfo = {
113263 isLoaded ?: boolean ;
114264} ;
115265
116- export type SessionManagerOptions = {
266+ type SessionManagerOptions = {
117267 projectRoot : string ;
118268 createOpenAIClient : CreateOpenAIClient ;
119269 getResolvedSettings : ( ) => {
@@ -138,140 +288,6 @@ export type LlmStreamProgress = {
138288 formattedTokens : string ;
139289 phase : "start" | "update" | "end" ;
140290} ;
141- import { DEEPSEEK_V4_MODELS } from "./common/model-capabilities" ;
142-
143- const DEFAULT_COMPACT_PROMPT_TOKEN_THRESHOLD = 128 * 1024 ;
144- const DEEPSEEK_V4_COMPACT_PROMPT_TOKEN_THRESHOLD = 512 * 1024 ;
145-
146- export function getCompactPromptTokenThreshold ( model : string ) : number {
147- return DEEPSEEK_V4_MODELS . has ( model )
148- ? DEEPSEEK_V4_COMPACT_PROMPT_TOKEN_THRESHOLD
149- : DEFAULT_COMPACT_PROMPT_TOKEN_THRESHOLD ;
150- }
151-
152- export function isUsageRecord ( value : unknown ) : value is Record < string , unknown > {
153- return value !== null && typeof value === "object" && ! Array . isArray ( value ) ;
154- }
155-
156- export function getTotalTokens ( usage : ModelUsage | null | undefined ) : number {
157- if ( ! isUsageRecord ( usage ) ) {
158- return 0 ;
159- }
160- const totalTokens = ( usage as Record < string , unknown > ) . total_tokens ;
161- return typeof totalTokens === "number" ? totalTokens : 0 ;
162- }
163-
164- export function summarizeCompletionOptions ( options ?: Record < string , unknown > ) : Record < string , unknown > | undefined {
165- if ( ! options ) {
166- return undefined ;
167- }
168- return {
169- ...options ,
170- signal : options . signal instanceof AbortSignal ? { aborted : options . signal . aborted } : options . signal ,
171- } ;
172- }
173-
174- export function addUsageValue ( current : unknown , next : unknown ) : unknown {
175- if ( typeof next === "number" ) {
176- return ( typeof current === "number" ? current : 0 ) + next ;
177- }
178-
179- if ( isUsageRecord ( next ) ) {
180- const currentRecord = isUsageRecord ( current ) ? current : { } ;
181- const result : Record < string , unknown > = { ...currentRecord } ;
182- for ( const [ key , value ] of Object . entries ( next ) ) {
183- result [ key ] = addUsageValue ( currentRecord [ key ] , value ) ;
184- }
185- return result ;
186- }
187-
188- return next ;
189- }
190-
191- export function accumulateUsage ( current : ModelUsage | null , next : unknown | null | undefined ) : ModelUsage | null {
192- if ( next == null ) {
193- return current ?? null ;
194- }
195- return addUsageValue ( current , next ) as ModelUsage ;
196- }
197-
198- export function usageWithRequestCount ( usage : ModelUsage ) : ModelUsage {
199- const totalReqs = typeof usage . total_reqs === "number" ? usage . total_reqs + 1 : 1 ;
200- return {
201- ...usage ,
202- total_reqs : totalReqs ,
203- } ;
204- }
205-
206- export function accumulateUsagePerModel (
207- current : Record < string , ModelUsage > | null | undefined ,
208- model : string ,
209- next : ModelUsage | null | undefined
210- ) : Record < string , ModelUsage > | null {
211- if ( next == null ) {
212- return current ?? null ;
213- }
214-
215- const usagePerModel = { ...( current ?? { } ) } ;
216- const modelName = model . trim ( ) || "unknown" ;
217- usagePerModel [ modelName ] = accumulateUsage ( usagePerModel [ modelName ] ?? null , usageWithRequestCount ( next ) ) ! ;
218- return usagePerModel ;
219- }
220-
221- export { getExtensionRoot } from "./prompt" ;
222- import * as fs from "fs" ;
223- import * as path from "path" ;
224- import * as os from "os" ;
225- import * as crypto from "crypto" ;
226- import matter from "gray-matter" ;
227- import ejs from "ejs" ;
228- import type { ChatCompletionContentPart , ChatCompletionMessageParam } from "openai/resources/chat/completions" ;
229- import { launchNotifyScript } from "./common/notify" ;
230- import { buildThinkingRequestOptions } from "./common/openai-thinking" ;
231- import { supportsMultimodal } from "./common/model-capabilities" ;
232- import {
233- getCompactPrompt ,
234- getDefaultSkillPrompt ,
235- getExtensionRoot ,
236- getRuntimeContext ,
237- getSystemPrompt ,
238- getTools ,
239- type ToolDefinition ,
240- } from "./prompt" ;
241- import {
242- type ProcessTimeoutControl ,
243- type ProcessTimeoutInfo ,
244- type ToolCallExecution ,
245- type ToolExecutionHooks ,
246- ToolExecutor ,
247- } from "./tools/executor" ;
248- import { McpManager } from "./mcp/mcp-manager" ;
249-
250- import { logApiError } from "./common/error-logger" ;
251- import { logOpenAIChatCompletionDebug , normalizeDebugError } from "./common/debug-logger" ;
252- import { killProcessTree } from "./common/process-tree" ;
253- import { GitFileHistory } from "./common/file-history" ;
254- import { getSnippet } from "./common/state" ;
255- import {
256- appendProjectPermissionAllows ,
257- buildPermissionToolExecution ,
258- computeToolCallPermissions ,
259- hasUserPermissionReplies ,
260- normalizeAskPermissions ,
261- parseToolCallForPermissions ,
262- type PermissionToolCall ,
263- } from "./common/permissions" ;
264-
265- const MAX_SESSION_ENTRIES = 50 ;
266- const DEFAULT_NEW_PROMPT_API_URL = "https://deepcode.vegamo.cn/api/plugin/new" ;
267- const NEW_PROMPT_REPORT_TIMEOUT_MS = 3000 ;
268-
269- type ChatCompletionDebugOptions = {
270- enabled ?: boolean ;
271- location : string ;
272- baseURL ?: string ;
273- params ?: Record < string , unknown > ;
274- } ;
275291
276292export class SessionManager {
277293 private readonly projectRoot : string ;
0 commit comments