@jetcrab/pi-desk 1.1.23-dev → 1.1.24-dev
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/docs/commands.md +4 -2
- package/docs/plugins.md +2 -0
- package/package.json +1 -1
- package/src/server/l1_entry/api/l1-plugin-management-route.ts +0 -4
- package/src/server/l2_biz/plugin-management/l2-plugin-management.ts +4 -37
- package/src/server/l2_biz/plugin-management/l2-plugin-updates.ts +1 -4
- package/src/server/l2_biz/work-session/l2-work-session-chat-runtime.ts +4 -2
- package/src/server/l4_foundation/pi/l4-pi-chat-worker.ts +16 -1
- package/src/server/l4_foundation/pi/l4-pi-package-root-consistency.ts +3 -6
- package/src/server/l4_foundation/pi/l4-pi-package-root-gate.ts +1 -1
- package/src/server/l4_foundation/pi/l4-pi-plugin-owner-runtime.ts +4 -0
- package/src/server/l4_foundation/pi/l4-pi-plugin-sources.ts +5 -1
- package/src/server/l4_foundation/pi/l4-pi-session-initialization.ts +30 -34
- package/src/server/l4_foundation/pi/l4-pi-session-resources.ts +15 -14
- package/src/server/l4_foundation/pi/l4-pi-work-session-runtime.ts +1 -1
- package/src/server/l4_foundation/skills/runtime/l4-skill-catalog.ts +159 -166
- package/temp/build/pi-desk/release/.next/BUILD_ID +1 -1
- package/temp/build/pi-desk/release/.next/build-manifest.json +2 -2
- package/temp/build/pi-desk/release/.next/required-server-files.js +2 -2
- package/temp/build/pi-desk/release/.next/required-server-files.json +2 -2
- package/temp/build/pi-desk/release/.next/server/app/_global-error/page_client-reference-manifest.js +1 -1
- package/temp/build/pi-desk/release/.next/server/app/_global-error.html +1 -1
- package/temp/build/pi-desk/release/.next/server/app/_global-error.rsc +1 -1
- package/temp/build/pi-desk/release/.next/server/app/_global-error.segments/_full.segment.rsc +1 -1
- package/temp/build/pi-desk/release/.next/server/app/_global-error.segments/_global-error/__PAGE__.segment.rsc +1 -1
- package/temp/build/pi-desk/release/.next/server/app/_global-error.segments/_tree.segment.rsc +1 -1
- package/temp/build/pi-desk/release/.next/server/app/_not-found/page_client-reference-manifest.js +1 -1
- package/temp/build/pi-desk/release/.next/server/app/api/health/route.js +1 -1
- package/temp/build/pi-desk/release/.next/server/app/api/plugins/add/route.js +1 -1
- package/temp/build/pi-desk/release/.next/server/app/api/plugins/apply/route.js +1 -1
- package/temp/build/pi-desk/release/.next/server/app/api/plugins/batch/route.js +1 -1
- package/temp/build/pi-desk/release/.next/server/app/api/plugins/del/route.js +1 -1
- package/temp/build/pi-desk/release/.next/server/app/api/plugins/enabled-set/route.js +1 -1
- package/temp/build/pi-desk/release/.next/server/app/api/plugins/get/route.js +1 -1
- package/temp/build/pi-desk/release/.next/server/app/api/plugins/list/route.js +1 -1
- package/temp/build/pi-desk/release/.next/server/app/api/plugins/reload/route.js +1 -1
- package/temp/build/pi-desk/release/.next/server/app/api/plugins/update/route.js +1 -1
- package/temp/build/pi-desk/release/.next/server/app/login/page_client-reference-manifest.js +1 -1
- package/temp/build/pi-desk/release/.next/server/app/page.js +1 -1
- package/temp/build/pi-desk/release/.next/server/app/page_client-reference-manifest.js +1 -1
- package/temp/build/pi-desk/release/.next/server/app/project-preview/page_client-reference-manifest.js +1 -1
- package/temp/build/pi-desk/release/.next/server/chunks/16438.js +1 -1
- package/temp/build/pi-desk/release/.next/server/chunks/18748.js +1 -0
- package/temp/build/pi-desk/release/.next/server/chunks/24557.js +1 -1
- package/temp/build/pi-desk/release/.next/server/chunks/25406.js +13 -14
- package/temp/build/pi-desk/release/.next/server/chunks/36361.js +2 -2
- package/temp/build/pi-desk/release/.next/server/chunks/50520.js +2 -2
- package/temp/build/pi-desk/release/.next/server/chunks/6917.js +1 -1
- package/temp/build/pi-desk/release/.next/server/chunks/80290.js +1 -1
- package/temp/build/pi-desk/release/.next/server/chunks/92386.js +15 -16
- package/temp/build/pi-desk/release/.next/server/instrumentation.js +1 -1
- package/temp/build/pi-desk/release/.next/server/middleware-build-manifest.js +1 -1
- package/temp/build/pi-desk/release/.next/server/pages/500.html +1 -1
- package/temp/build/pi-desk/release/.next/static/chunks/app/{page-54040b9fd54ee8b3.js → page-ff44dcf27c2ba1c2.js} +1 -1
- package/temp/build/pi-desk/release/.next/trace-build +1 -1
- package/temp/build/pi-desk/release/.next/server/chunks/93049.js +0 -1
- /package/temp/build/pi-desk/release/.next/static/{gAYTx4KJJR3vTtGYexj4J → w3CeIboVupqJdq890TlcG}/_buildManifest.js +0 -0
- /package/temp/build/pi-desk/release/.next/static/{gAYTx4KJJR3vTtGYexj4J → w3CeIboVupqJdq890TlcG}/_ssgManifest.js +0 -0
package/docs/commands.md
CHANGED
|
@@ -71,6 +71,8 @@
|
|
|
71
71
|
|
|
72
72
|
接纳后结束当前轮;宿主等本会话完全空闲后重载,再通知并唤醒本会话。重载可能重建插件内存状态、使缓存失效,配置和工具声明变化也可能影响提示词缓存。
|
|
73
73
|
|
|
74
|
-
|
|
74
|
+
插件管理最多等待目标来源的在途调用 15 秒;超过时仅该项维护失败,可稍后手动重试,不冻结常规聊天或阻止后续其他来源维护。完成通知表示包和宿主能力已应用;新建会话使用最新工具配置,已有原生 Pi 会话不自动重载。Node/Browser 替换会重新挂载目标插件界面,旧调用可能失效;结果未知的副作用请求不自动重发。
|
|
75
75
|
|
|
76
|
-
|
|
76
|
+
维护期间或失败后允许目标包暂时缺失。会话跳过缺失或版本不符的包,明确的单插件加载/启动错误在插件管理中保留诊断,不因此让其他正常插件一起降级。聊天订阅、刷新和重连不等待包维护或插件初始化;未就绪的模型与插件状态稍后同步。修复包后按需重载目标会话,不自动重载已有会话。
|
|
77
|
+
|
|
78
|
+
需要重启时按插件管理界面的维护流程处理,只延后受影响来源,以最终结果为准。
|
package/docs/plugins.md
CHANGED
|
@@ -339,4 +339,6 @@ try {
|
|
|
339
339
|
|
|
340
340
|
## 更新生效
|
|
341
341
|
|
|
342
|
+
包维护不阻塞聊天订阅、刷新或其他会话。缺失或版本不符的包会被跳过,导入/工厂失败的入口不加载;Pi 生命周期回调和插件 Provider 注册的明确错误保留在插件管理诊断中,不升级为整会话失败。正常扩展继续加载。此隔离不终止同步死循环,也不回滚插件已发生的外部副作用;整体初始化超时仍可能使当前会话回退基础聊天,不阻塞其他会话的历史同步。
|
|
343
|
+
|
|
342
344
|
`plugins install/reload` 应用 Node/Browser 能力;目标插件活动视图重挂,旧 Browser Owner 失效。现有 Native 会话不自动重载,新 UI 可能连接旧会话方法,需兼容既有消息和方法数据;需要现有会话加载当前原生代码时使用 `session reload`。热加载无法处理的资源按宿主已有重启流程处理,不把重新挂载界面当作后端任务已停止。
|
package/package.json
CHANGED
|
@@ -15,7 +15,6 @@ import {
|
|
|
15
15
|
L2PluginManagementInvalidPackageError,
|
|
16
16
|
L2PluginManagementNotFoundError,
|
|
17
17
|
L2PluginManagementOperationConflictError,
|
|
18
|
-
L2PluginManagementPackageRootError,
|
|
19
18
|
L2PluginManagementRestartPendingError,
|
|
20
19
|
L2PluginManagementRestartUnavailableError
|
|
21
20
|
} from '@server/l2_biz/plugin-management/l2-plugin-management'
|
|
@@ -43,9 +42,6 @@ function pluginOperationError(error: unknown, action: string): Response {
|
|
|
43
42
|
) {
|
|
44
43
|
return createL4ApiErrorResponse(409, error.message)
|
|
45
44
|
}
|
|
46
|
-
if (error instanceof L2PluginManagementPackageRootError) {
|
|
47
|
-
return createL4ApiErrorResponse(409, error.message, error.rootError.i18n)
|
|
48
|
-
}
|
|
49
45
|
if (error instanceof L2PluginReloadBlockedError) {
|
|
50
46
|
return createL4ApiErrorResponse(409, error.message, {
|
|
51
47
|
key: 'errors:pluginReloadBlocked',
|
|
@@ -136,13 +136,6 @@ export class L2PluginManagementInvalidPackageError extends Error {
|
|
|
136
136
|
}
|
|
137
137
|
}
|
|
138
138
|
|
|
139
|
-
export class L2PluginManagementPackageRootError extends Error {
|
|
140
|
-
constructor(readonly rootError: L4PiPackageRootConsistencyError) {
|
|
141
|
-
super(rootError.message)
|
|
142
|
-
this.name = 'L2PluginManagementPackageRootError'
|
|
143
|
-
}
|
|
144
|
-
}
|
|
145
|
-
|
|
146
139
|
export class L2PluginManagementRestartPendingError extends Error {
|
|
147
140
|
constructor() {
|
|
148
141
|
super('已有插件操作需要重启完成')
|
|
@@ -845,12 +838,6 @@ export class L2PluginManagement {
|
|
|
845
838
|
let updateTag: string | null = null
|
|
846
839
|
let pluginName: string | null = null
|
|
847
840
|
if (mutation !== 'enable' && mutation !== 'disable') {
|
|
848
|
-
const packageRootError = await readL4PiPackageRootConsistencyError({
|
|
849
|
-
agentDir: this.agentDir,
|
|
850
|
-
configured,
|
|
851
|
-
repairSource: mutation === 'add' && reinstall ? operationSource : undefined
|
|
852
|
-
})
|
|
853
|
-
if (packageRootError) throw new L2PluginManagementPackageRootError(packageRootError)
|
|
854
841
|
if (mutation !== 'add' && !target) throw new L2PluginManagementNotFoundError(source)
|
|
855
842
|
this.setOperation(operationSource, 'checking', '正在确认插件版本和下载源')
|
|
856
843
|
const candidate = this.candidateSource(source, target?.installedPath)
|
|
@@ -899,29 +886,11 @@ export class L2PluginManagement {
|
|
|
899
886
|
setL4PiPluginUpdateTag(operationSource, updateTag, this.agentDir)
|
|
900
887
|
this.updates.clear(operationSource)
|
|
901
888
|
}
|
|
902
|
-
if (this.deferredMaintenance.size > 0) {
|
|
903
|
-
this.deferredMaintenance.set(operationSource, request)
|
|
904
|
-
waiting('等待重启后完成安装维护')
|
|
905
|
-
return
|
|
906
|
-
}
|
|
907
889
|
}
|
|
908
890
|
this.assertOpen()
|
|
909
891
|
await runtime.withSourceIdle(
|
|
910
892
|
operationSource,
|
|
911
893
|
async () => {
|
|
912
|
-
if (request) {
|
|
913
|
-
const current = this.createPackageManager()
|
|
914
|
-
this.throwSettingsErrors(current.settingsManager)
|
|
915
|
-
const configured = current.packageManager
|
|
916
|
-
.listConfiguredPackages()
|
|
917
|
-
.filter((item) => item.scope === 'user')
|
|
918
|
-
const rootError = await readL4PiPackageRootConsistencyError({
|
|
919
|
-
agentDir: this.agentDir,
|
|
920
|
-
configured,
|
|
921
|
-
repairSource: mutation === 'add' && input.reinstall ? operationSource : undefined
|
|
922
|
-
})
|
|
923
|
-
if (rootError) throw new L2PluginManagementPackageRootError(rootError)
|
|
924
|
-
}
|
|
925
894
|
this.assertOpen()
|
|
926
895
|
this.setOperation(
|
|
927
896
|
operationSource,
|
|
@@ -957,7 +926,8 @@ export class L2PluginManagement {
|
|
|
957
926
|
await runL4PiPackageMaintenance({
|
|
958
927
|
maintenance: request!,
|
|
959
928
|
agentDir: this.agentDir,
|
|
960
|
-
cwd: this.packageManagerCwd
|
|
929
|
+
cwd: this.packageManagerCwd,
|
|
930
|
+
signal: this.lifetime.signal
|
|
961
931
|
})
|
|
962
932
|
if (mutation !== 'del') await this.verifyInstalledPackage(request!.source)
|
|
963
933
|
})
|
|
@@ -1372,11 +1342,8 @@ export class L2PluginManagement {
|
|
|
1372
1342
|
if (!force && this.capabilityCache) return this.capabilityCache
|
|
1373
1343
|
|
|
1374
1344
|
try {
|
|
1375
|
-
this.
|
|
1376
|
-
|
|
1377
|
-
if (!force && this.capabilityCache) return Promise.resolve(this.capabilityCache)
|
|
1378
|
-
return runL4PiPluginCapabilityProbe(this.cwd, this.agentDir)
|
|
1379
|
-
})
|
|
1345
|
+
this.assertOpen()
|
|
1346
|
+
this.capabilityCache = await runL4PiPluginCapabilityProbe(this.cwd, this.agentDir)
|
|
1380
1347
|
this.publish()
|
|
1381
1348
|
console.info('[Pi Desk][PluginManagement] 插件能力盘点完成', {
|
|
1382
1349
|
packageCount: this.capabilityCache.packages.length,
|
|
@@ -20,7 +20,6 @@ import {
|
|
|
20
20
|
resolveL4PluginInstall
|
|
21
21
|
} from '@server/l4_foundation/pi/l4-pi-plugin-registry'
|
|
22
22
|
import type { L4PiPluginSource } from '@server/l4_foundation/pi/l4-pi-plugin-sources'
|
|
23
|
-
import { runL4PiPackageRootExclusive } from '@server/l4_foundation/pi/l4-pi-package-root-gate'
|
|
24
23
|
|
|
25
24
|
const UPDATE_CACHE_TTL_MS = 5 * 60 * 1000
|
|
26
25
|
const UPDATE_CACHE_MAX_ENTRIES = 128
|
|
@@ -163,9 +162,7 @@ export class L2PluginUpdates {
|
|
|
163
162
|
cwd: this.cwd,
|
|
164
163
|
settingsManager
|
|
165
164
|
})
|
|
166
|
-
const updates = await
|
|
167
|
-
manager.checkForAvailableUpdates()
|
|
168
|
-
)
|
|
165
|
+
const updates = await manager.checkForAvailableUpdates()
|
|
169
166
|
const value: L2PluginUpdateStatus = {
|
|
170
167
|
...empty,
|
|
171
168
|
updateAvailable: updates.some((item) => item.scope === 'user')
|
|
@@ -359,17 +359,19 @@ export class L2WorkSessionChatRuntime {
|
|
|
359
359
|
|
|
360
360
|
prepareSource(sourceInput: L2ChatSource): Promise<void> {
|
|
361
361
|
const record = this.currentRecord(sourceInput)
|
|
362
|
-
|
|
362
|
+
// 快照读取不能排在重载、原生命令或插件初始化等修改操作后面。
|
|
363
|
+
return this.syncRecordRuntime(record)
|
|
363
364
|
}
|
|
364
365
|
|
|
365
366
|
private async syncRecordRuntime(record: L2WorkSessionChatRecord): Promise<void> {
|
|
366
367
|
const source = record.source
|
|
368
|
+
const version = record.sourceEventVersion
|
|
367
369
|
const runtime = await getL4PiWorkSessionRuntime(source.sessionId).readChatRuntime()
|
|
368
370
|
const nextRuntime = L2ChatRuntimeSchema.parse({
|
|
369
371
|
...runtime,
|
|
370
372
|
presentationMode: record.state.runtime.presentationMode
|
|
371
373
|
})
|
|
372
|
-
if (!this.isCurrentSource(source)) return
|
|
374
|
+
if (!this.isCurrentSource(source) || record.sourceEventVersion !== version) return
|
|
373
375
|
if (!sameRuntime(record.state.runtime, nextRuntime))
|
|
374
376
|
this.applySourceEvent(source, { type: 'runtime_update', runtime: nextRuntime })
|
|
375
377
|
}
|
|
@@ -769,7 +769,22 @@ export class L4PiChatWorker {
|
|
|
769
769
|
return () => this.listeners.delete(listener)
|
|
770
770
|
}
|
|
771
771
|
|
|
772
|
-
async getRuntime(): Promise<L4PiChatRuntimeSnapshot> {
|
|
772
|
+
async getRuntime(waitForInitialization = true): Promise<L4PiChatRuntimeSnapshot> {
|
|
773
|
+
if (!waitForInitialization) {
|
|
774
|
+
if (this.disposed) throw new Error('Pi chat worker has been disposed')
|
|
775
|
+
// 订阅/重连先交付当前快照;初始化结果沿已有 runtime_changed 独立推送。
|
|
776
|
+
if (!this.session && !this.sessionLifecycle) {
|
|
777
|
+
void this.ensureSession().catch((cause: unknown) => {
|
|
778
|
+
if (this.disposed || this.session) return
|
|
779
|
+
this.initializationError = (cause instanceof Error ? cause.message : String(cause)).slice(
|
|
780
|
+
0,
|
|
781
|
+
32_000
|
|
782
|
+
)
|
|
783
|
+
this.emit({ type: 'runtime_changed', runtime: this.runtimeSnapshot() })
|
|
784
|
+
})
|
|
785
|
+
}
|
|
786
|
+
return this.runtimeSnapshot()
|
|
787
|
+
}
|
|
773
788
|
const initialization = this.ensureSession()
|
|
774
789
|
let release = (): void => undefined
|
|
775
790
|
const interaction = new Promise<void>((resolve) => {
|
|
@@ -37,7 +37,6 @@ async function directoryExists(path: string): Promise<boolean> {
|
|
|
37
37
|
export async function readL4PiPackageRootConsistencyError(input: {
|
|
38
38
|
agentDir: string
|
|
39
39
|
configured: readonly L4PiConfiguredPackageLocation[]
|
|
40
|
-
repairSource?: string
|
|
41
40
|
}): Promise<L4PiPackageRootConsistencyError | null> {
|
|
42
41
|
const npmPackages = input.configured.flatMap((item) => {
|
|
43
42
|
const name = npmPackageName(item.source)
|
|
@@ -63,14 +62,12 @@ export async function readL4PiPackageRootConsistencyError(input: {
|
|
|
63
62
|
}
|
|
64
63
|
}
|
|
65
64
|
|
|
66
|
-
//
|
|
67
|
-
const
|
|
68
|
-
const requiredPackages = npmPackages.filter((item) => item.name !== repairName)
|
|
69
|
-
const expected = new Set(requiredPackages.map((item) => item.name))
|
|
65
|
+
// 只用于管理页诊断,不作为其他插件操作或聊天的前置条件。
|
|
66
|
+
const expected = new Set(npmPackages.map((item) => item.name))
|
|
70
67
|
const actual = new Set(Object.keys(dependencies))
|
|
71
68
|
const missing = [...expected].filter((name) => !actual.has(name))
|
|
72
69
|
const missingDirectories: string[] = []
|
|
73
|
-
for (const { name, item } of
|
|
70
|
+
for (const { name, item } of npmPackages) {
|
|
74
71
|
if (!item.installedPath || !(await directoryExists(item.installedPath))) {
|
|
75
72
|
missingDirectories.push(name)
|
|
76
73
|
}
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import 'server-only'
|
|
2
2
|
|
|
3
|
-
/**
|
|
3
|
+
/** 只串行安装目录写入;只读发现、聊天与扩展生命周期不得等待此锁。 */
|
|
4
4
|
export function runL4PiPackageRootExclusive<T>(operation: () => Promise<T>): Promise<T> {
|
|
5
5
|
const previous = globalThis.__piDeskPackageRootTail ?? Promise.resolve()
|
|
6
6
|
const result = previous.then(operation, operation)
|
|
@@ -411,8 +411,12 @@ export class L4PiPluginOwnerRuntime {
|
|
|
411
411
|
}
|
|
412
412
|
this.cancelRecovery(source)
|
|
413
413
|
this.maintainingSources.add(source)
|
|
414
|
+
const deadline = performance.now() + 15_000
|
|
414
415
|
try {
|
|
415
416
|
while (this.owners.some((owner) => owner.source === source && owner.activeCalls.size > 0)) {
|
|
417
|
+
if (performance.now() >= deadline) {
|
|
418
|
+
throw new Error(`插件 ${source} 的在途调用未在15秒内结束,本次维护失败,可稍后手动重试`)
|
|
419
|
+
}
|
|
416
420
|
onWaiting(`插件 ${source} 仍有调用正在执行`)
|
|
417
421
|
await delay(250, undefined, { signal: lifetime })
|
|
418
422
|
}
|
|
@@ -160,7 +160,11 @@ export async function discoverL4PiPluginSources(
|
|
|
160
160
|
|
|
161
161
|
try {
|
|
162
162
|
// 显式 skip,列清单不能触发缺失包安装、扩展导入或 AgentSession。
|
|
163
|
-
const resolved = await manager.resolve(async () =>
|
|
163
|
+
const resolved = await manager.resolve(async (source) => {
|
|
164
|
+
const item = sources.get(source)
|
|
165
|
+
if (item) item.error ??= '插件包未安装或版本不匹配'
|
|
166
|
+
return 'skip'
|
|
167
|
+
})
|
|
164
168
|
for (const item of sources.values()) item.hasPiResources = false
|
|
165
169
|
for (const resource of [
|
|
166
170
|
...resolved.extensions,
|
|
@@ -32,7 +32,7 @@ import { L4PiUiRuntime } from './l4-pi-ui-runtime'
|
|
|
32
32
|
const INITIALIZATION_TIMEOUT_MS = 60_000
|
|
33
33
|
const CLEANUP_TIMEOUT_MS = 1_500
|
|
34
34
|
const MAX_RETIRED_INITIALIZATIONS = 4
|
|
35
|
-
const retiredInitializations = new Set<Promise<unknown
|
|
35
|
+
const retiredInitializations = new WeakMap<L4PiSessionResources, Set<Promise<unknown>>>()
|
|
36
36
|
const STALE_MESSAGE = 'Pi 会话实例已失效,不能继续访问当前会话'
|
|
37
37
|
|
|
38
38
|
interface InitializationOptions {
|
|
@@ -57,6 +57,7 @@ export class L4PiSessionInitialization {
|
|
|
57
57
|
readonly ui: L4PiUiRuntime
|
|
58
58
|
private readonly manager: SessionManager
|
|
59
59
|
private readonly sessionId: string
|
|
60
|
+
private readonly retired: Set<Promise<unknown>>
|
|
60
61
|
private active = true
|
|
61
62
|
private accessRevoked = false
|
|
62
63
|
private session: AgentSession | null = null
|
|
@@ -68,6 +69,9 @@ export class L4PiSessionInitialization {
|
|
|
68
69
|
|
|
69
70
|
constructor(private readonly options: InitializationOptions) {
|
|
70
71
|
this.sessionId = options.sessionManager.getSessionId()
|
|
72
|
+
// 只限制同一工作会话的重复尝试,不能因其他会话的插件清理挂起而拒绝加载。
|
|
73
|
+
this.retired = retiredInitializations.get(options.resources) ?? new Set()
|
|
74
|
+
retiredInitializations.set(options.resources, this.retired)
|
|
71
75
|
this.ui = options.ui ?? new L4PiUiRuntime()
|
|
72
76
|
this.capabilities = new L4PiCapabilityRuntime(options.readCapabilities)
|
|
73
77
|
this.plugins = new L4PiSessionPluginRuntime({
|
|
@@ -116,29 +120,20 @@ export class L4PiSessionInitialization {
|
|
|
116
120
|
async reload(): Promise<void> {
|
|
117
121
|
this.assertActive()
|
|
118
122
|
const session = await this.start()
|
|
119
|
-
let failure: Error | null = null
|
|
120
|
-
this.onLifecycleError = (error) => {
|
|
121
|
-
failure ??= error
|
|
122
|
-
}
|
|
123
123
|
this.setStage('重载 Pi 配置')
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
this.assertActive()
|
|
138
|
-
if (failure) throw failure
|
|
139
|
-
} finally {
|
|
140
|
-
this.onLifecycleError = null
|
|
141
|
-
}
|
|
124
|
+
await session.reload({
|
|
125
|
+
beforeSessionStart: async () => {
|
|
126
|
+
this.assertActive()
|
|
127
|
+
this.validateResources(
|
|
128
|
+
session.resourceLoader,
|
|
129
|
+
session.settingsManager.drainErrors().map(({ error }) => ({
|
|
130
|
+
type: 'error',
|
|
131
|
+
message: error.message
|
|
132
|
+
}))
|
|
133
|
+
)
|
|
134
|
+
}
|
|
135
|
+
})
|
|
136
|
+
this.assertActive()
|
|
142
137
|
}
|
|
143
138
|
|
|
144
139
|
setStage(stage: string): void {
|
|
@@ -154,11 +149,8 @@ export class L4PiSessionInitialization {
|
|
|
154
149
|
private async initialize(refreshResources: boolean): Promise<AgentSession> {
|
|
155
150
|
const startedAt = performance.now()
|
|
156
151
|
this.assertActive()
|
|
157
|
-
if (
|
|
158
|
-
|
|
159
|
-
retiredInitializations.size >= MAX_RETIRED_INITIALIZATIONS
|
|
160
|
-
) {
|
|
161
|
-
throw new Error('尚未结束的插件初始化或关闭任务过多,请重启服务后再加载插件')
|
|
152
|
+
if (this.options.mode === 'normal' && this.retired.size >= MAX_RETIRED_INITIALIZATIONS) {
|
|
153
|
+
throw new Error('当前会话尚未结束的插件初始化或关闭任务过多,可先使用其他会话')
|
|
162
154
|
}
|
|
163
155
|
const failure = new Promise<never>((_, reject) => {
|
|
164
156
|
this.onLifecycleError = reject
|
|
@@ -244,7 +236,7 @@ export class L4PiSessionInitialization {
|
|
|
244
236
|
)
|
|
245
237
|
: Promise.resolve()
|
|
246
238
|
const cleanup = Promise.allSettled([this.execution, shutdown])
|
|
247
|
-
|
|
239
|
+
this.retired.add(cleanup)
|
|
248
240
|
const timer = setTimeout(() => {
|
|
249
241
|
console.warn('[Pi Desk][PiSession] 旧实例仍在后台清理,不阻塞基础聊天', {
|
|
250
242
|
sessionId: this.sessionId,
|
|
@@ -255,7 +247,7 @@ export class L4PiSessionInitialization {
|
|
|
255
247
|
timer.unref()
|
|
256
248
|
void cleanup.then(() => {
|
|
257
249
|
clearTimeout(timer)
|
|
258
|
-
|
|
250
|
+
this.retired.delete(cleanup)
|
|
259
251
|
})
|
|
260
252
|
}
|
|
261
253
|
|
|
@@ -271,9 +263,13 @@ export class L4PiSessionInitialization {
|
|
|
271
263
|
resourceLoader: ResourceLoader,
|
|
272
264
|
diagnostics: AgentSessionServices['diagnostics']
|
|
273
265
|
): void {
|
|
274
|
-
|
|
275
|
-
|
|
276
|
-
|
|
266
|
+
// Pi 已将失败入口排除;保留局部诊断,不把其余可用扩展一起作废。
|
|
267
|
+
for (const { path, error } of resourceLoader.getExtensions().errors) {
|
|
268
|
+
const message = `${error}\n插件入口:${path}`
|
|
269
|
+
reportL4PiPluginNativeDiagnostic(this.options.cwd, message)
|
|
270
|
+
console.warn('[Pi Desk][PiSession] 已跳过失败扩展', { cwd: this.options.cwd, message })
|
|
271
|
+
}
|
|
272
|
+
const failures: string[] = []
|
|
277
273
|
for (const diagnostic of diagnostics) {
|
|
278
274
|
if (diagnostic.type === 'error') failures.push(diagnostic.message)
|
|
279
275
|
else console.info('[Pi Desk][PiSession] 初始化诊断', { cwd: this.options.cwd, ...diagnostic })
|
|
@@ -369,7 +365,7 @@ export class L4PiSessionInitialization {
|
|
|
369
365
|
sessionId: this.sessionId,
|
|
370
366
|
message: diagnostic
|
|
371
367
|
})
|
|
372
|
-
this.
|
|
368
|
+
reportL4PiPluginNativeDiagnostic(this.options.cwd, diagnostic)
|
|
373
369
|
if (error.event === 'session_tree' || error.event === 'session_before_tree') {
|
|
374
370
|
this.session?.extensionRunner
|
|
375
371
|
.getUIContext()
|
|
@@ -16,7 +16,7 @@ import {
|
|
|
16
16
|
type SourceInfo
|
|
17
17
|
} from '@earendil-works/pi-coding-agent'
|
|
18
18
|
import { clearL4PiPluginCodeCache, loadL4PiDeskPluginEntry } from './l4-pi-plugin-module-loader'
|
|
19
|
-
import {
|
|
19
|
+
import { reportL4PiPluginNativeDiagnostic } from './l4-pi-plugin-native-diagnostic'
|
|
20
20
|
import { createL4PiPluginDisabledFilter } from './l4-pi-plugin-disabled'
|
|
21
21
|
|
|
22
22
|
type LoaderOptions = ConstructorParameters<typeof DefaultResourceLoader>[0]
|
|
@@ -93,15 +93,8 @@ export class L4PiSessionResources {
|
|
|
93
93
|
onProgress?.('等待已选择的插件资源准备完成')
|
|
94
94
|
return this.selection
|
|
95
95
|
}
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
this.selection = runL4PiPackageRootExclusive(() => {
|
|
99
|
-
const durationMs = Math.round(performance.now() - queuedAt)
|
|
100
|
-
if (durationMs >= 1000) {
|
|
101
|
-
console.info('[Pi Desk][PluginResources] 插件资源队列等待结束', { cwd, durationMs })
|
|
102
|
-
}
|
|
103
|
-
return this.discover(cwd, agentDir, settings, onProgress)
|
|
104
|
-
})
|
|
96
|
+
// 维护允许暂时缺包;只读发现不能排在 npm 网络操作后面。
|
|
97
|
+
this.selection = this.discover(cwd, agentDir, settings, onProgress)
|
|
105
98
|
return this.selection
|
|
106
99
|
}
|
|
107
100
|
|
|
@@ -122,7 +115,10 @@ export class L4PiSessionResources {
|
|
|
122
115
|
})
|
|
123
116
|
const paths = await runLoadStage('发现已配置的插件与资源', onProgress, () =>
|
|
124
117
|
manager.resolve(async (source) => {
|
|
125
|
-
|
|
118
|
+
const message = `插件包未安装或版本不匹配,已跳过:${source}`
|
|
119
|
+
reportL4PiPluginNativeDiagnostic(cwd, message)
|
|
120
|
+
console.warn('[Pi Desk][PluginResources]', { cwd, message })
|
|
121
|
+
return 'skip'
|
|
126
122
|
})
|
|
127
123
|
)
|
|
128
124
|
const disabled = await createL4PiPluginDisabledFilter(agentDir, manager, settings)
|
|
@@ -307,25 +303,30 @@ export class L4PiSessionResources {
|
|
|
307
303
|
}
|
|
308
304
|
})
|
|
309
305
|
const runtime = resourceLoader.getExtensions().runtime
|
|
306
|
+
const reportProviderError = (extensionPath: string, cause: unknown): void => {
|
|
307
|
+
const message = `${extensionPath}: ${String(cause)}`
|
|
308
|
+
reportL4PiPluginNativeDiagnostic(cwd, message)
|
|
309
|
+
diagnostics.push({ type: 'warning', message })
|
|
310
|
+
}
|
|
310
311
|
for (const { name, config, extensionPath } of runtime.pendingProviderRegistrations) {
|
|
311
312
|
try {
|
|
312
313
|
modelRuntime.registerProvider(name, config)
|
|
313
314
|
} catch (cause) {
|
|
314
|
-
|
|
315
|
+
reportProviderError(extensionPath, cause)
|
|
315
316
|
}
|
|
316
317
|
}
|
|
317
318
|
for (const { provider, extensionPath } of runtime.pendingNativeProviderRegistrations) {
|
|
318
319
|
try {
|
|
319
320
|
modelRuntime.registerNativeProvider(provider)
|
|
320
321
|
} catch (cause) {
|
|
321
|
-
|
|
322
|
+
reportProviderError(extensionPath, cause)
|
|
322
323
|
}
|
|
323
324
|
}
|
|
324
325
|
for (const { definition, extensionPath } of runtime.pendingVirtualModelRegistrations) {
|
|
325
326
|
try {
|
|
326
327
|
modelRuntime.registerVirtualModel(definition)
|
|
327
328
|
} catch (cause) {
|
|
328
|
-
|
|
329
|
+
reportProviderError(extensionPath, cause)
|
|
329
330
|
}
|
|
330
331
|
}
|
|
331
332
|
runtime.pendingProviderRegistrations = []
|
|
@@ -215,7 +215,7 @@ export class L4PiWorkSessionRuntime {
|
|
|
215
215
|
|
|
216
216
|
async readChatRuntime(): Promise<L4PiChatRuntimeSnapshot> {
|
|
217
217
|
this.assertActive()
|
|
218
|
-
return this.chatWorker.getRuntime()
|
|
218
|
+
return this.chatWorker.getRuntime(false)
|
|
219
219
|
}
|
|
220
220
|
|
|
221
221
|
async readModelContext(): Promise<L4PiModelContext> {
|