@chainingintention/pi-web-cn 1.202608.9 → 1.202608.11
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/README.md +2 -0
- package/dist/cli.js +123 -72
- package/dist/cli.js.map +1 -1
- package/dist/client/assets/{CodeViewer-B9BLGnQ2.js → CodeViewer-DCl56K0Z.js} +2 -2
- package/dist/client/assets/{TerminalPanel-C1BWg5W2.js → TerminalPanel-DCvoEjUF.js} +6 -6
- package/dist/client/assets/{index-x7EWN9ro.js → index-CMvms5cF.js} +534 -512
- package/dist/client/index.html +1 -1
- package/dist/config.js +39 -18
- package/dist/config.js.map +1 -1
- package/dist/nativeServices/serviceAction.js +128 -0
- package/dist/nativeServices/serviceAction.js.map +1 -0
- package/dist/pi-web-plugins/git/browser/git-contract.js +108 -0
- package/dist/pi-web-plugins/git/browser/git-panel.js +609 -0
- package/dist/pi-web-plugins/git/browser/gitFileList.js +43 -0
- package/dist/pi-web-plugins/git/browser/gitFileShared.js +16 -0
- package/dist/pi-web-plugins/git/browser/gitFileTree.js +81 -0
- package/dist/pi-web-plugins/git/browser/gitFileViewPreference.js +35 -0
- package/dist/pi-web-plugins/git/browser/gitRoute.js +42 -0
- package/dist/pi-web-plugins/git/browser/pi-web-plugin.js +10 -0
- package/dist/pi-web-plugins/git/browser/unifiedDiff.js +301 -0
- package/dist/pi-web-plugins/git/git-backend.js +433 -0
- package/dist/pi-web-plugins/git/package.json +15 -0
- package/dist/pi-web-plugins/git/server-plugin.js +176 -0
- package/dist/pi-web-plugins/info/infoInternals.js +1 -1
- package/dist/pi-web-plugins/info/pi-web-plugin.js +2 -2
- package/dist/pi-web-plugins/relays/pi-web-plugin.js +1 -1
- package/dist/pi-web-plugins/relays/relayDiscovery.js +128 -23
- package/dist/pi-web-plugins/relays/relaysPanelElement.js +138 -16
- package/dist/pi-web-plugins/updates/pi-web-plugin.js +1 -1
- package/dist/pi-web-plugins/workspace-tasks/pi-web-plugin.js +1 -1
- package/dist/piWebVersionReport.js +63 -4
- package/dist/piWebVersionReport.js.map +1 -1
- package/dist/plugin-api.d.ts +40 -18
- package/dist/pluginRecoveryCli.js +172 -0
- package/dist/pluginRecoveryCli.js.map +1 -0
- package/dist/server/activity/workspaceActivityService.js +7 -1
- package/dist/server/activity/workspaceActivityService.js.map +1 -1
- package/dist/server/app.js +49 -23
- package/dist/server/app.js.map +1 -1
- package/dist/server/configRoutes.js +4 -5
- package/dist/server/configRoutes.js.map +1 -1
- package/dist/server/machines/machineClient.js +23 -4
- package/dist/server/machines/machineClient.js.map +1 -1
- package/dist/server/machines/machineProxyRoutes.js +162 -8
- package/dist/server/machines/machineProxyRoutes.js.map +1 -1
- package/dist/server/piWebPluginCatalog.js +441 -0
- package/dist/server/piWebPluginCatalog.js.map +1 -0
- package/dist/server/piWebPluginLifecycle.js +162 -0
- package/dist/server/piWebPluginLifecycle.js.map +1 -0
- package/dist/server/piWebPluginService.js +55 -7
- package/dist/server/piWebPluginService.js.map +1 -1
- package/dist/server/plugins/pluginBackendProxyRoutes.js +80 -0
- package/dist/server/plugins/pluginBackendProxyRoutes.js.map +1 -0
- package/dist/server/plugins/serverPluginExec.js +184 -0
- package/dist/server/plugins/serverPluginExec.js.map +1 -0
- package/dist/server/plugins/serverPluginRuntime.js +480 -0
- package/dist/server/plugins/serverPluginRuntime.js.map +1 -0
- package/dist/server/realtime/sessionEventHub.js +13 -0
- package/dist/server/realtime/sessionEventHub.js.map +1 -1
- package/dist/server/realtime/sessionEventScope.js +8 -1
- package/dist/server/realtime/sessionEventScope.js.map +1 -1
- package/dist/server/requestCancellation.js +28 -0
- package/dist/server/requestCancellation.js.map +1 -0
- package/dist/server/sessiond/agentProcessEnvironment.js +61 -0
- package/dist/server/sessiond/agentProcessEnvironment.js.map +1 -0
- package/dist/server/sessiond/managementProjectResolver.js +16 -0
- package/dist/server/sessiond/managementProjectResolver.js.map +1 -0
- package/dist/server/sessiond/pluginBackendRoutes.js +78 -0
- package/dist/server/sessiond/pluginBackendRoutes.js.map +1 -0
- package/dist/server/sessiond/sessionProxyRoutes.js +1 -0
- package/dist/server/sessiond/sessionProxyRoutes.js.map +1 -1
- package/dist/server/sessiond/sessionServiceDependencies.js +0 -1
- package/dist/server/sessiond/sessionServiceDependencies.js.map +1 -1
- package/dist/server/sessiond/sessiondStateOwnership.js +139 -0
- package/dist/server/sessiond/sessiondStateOwnership.js.map +1 -0
- package/dist/server/sessiond/workspaceCatalogRoutes.js +38 -0
- package/dist/server/sessiond/workspaceCatalogRoutes.js.map +1 -0
- package/dist/server/sessiond/workspaceRemovalRoutes.js +56 -0
- package/dist/server/sessiond/workspaceRemovalRoutes.js.map +1 -0
- package/dist/server/sessiond.js +98 -14
- package/dist/server/sessiond.js.map +1 -1
- package/dist/server/sessions/authService.js +15 -51
- package/dist/server/sessions/authService.js.map +1 -1
- package/dist/server/sessions/dockerEnvironmentFacts.js +181 -0
- package/dist/server/sessions/dockerEnvironmentFacts.js.map +1 -0
- package/dist/server/sessions/piSessionManagerGateway.js +129 -2
- package/dist/server/sessions/piSessionManagerGateway.js.map +1 -1
- package/dist/server/sessions/piSessionService.js +131 -97
- package/dist/server/sessions/piSessionService.js.map +1 -1
- package/dist/server/sessions/sessionCommandService.js +15 -1
- package/dist/server/sessions/sessionCommandService.js.map +1 -1
- package/dist/server/sessions/sessionEnvironmentFacts.js +48 -0
- package/dist/server/sessions/sessionEnvironmentFacts.js.map +1 -0
- package/dist/server/sessions/sessionFileFormat.js +27 -0
- package/dist/server/sessions/sessionFileFormat.js.map +1 -0
- package/dist/server/sessions/sessionFileHeader.js +82 -23
- package/dist/server/sessions/sessionFileHeader.js.map +1 -1
- package/dist/server/sessions/sessionRoutes.js +16 -2
- package/dist/server/sessions/sessionRoutes.js.map +1 -1
- package/dist/server/sessions/sessionSummaryScanner.js +491 -0
- package/dist/server/sessions/sessionSummaryScanner.js.map +1 -0
- package/dist/server/sessions/spawnSubsessionTool.js +4 -8
- package/dist/server/sessions/spawnSubsessionTool.js.map +1 -1
- package/dist/server/sessions/spawnTargetResolver.js +2 -2
- package/dist/server/sessions/spawnTargetResolver.js.map +1 -1
- package/dist/server/status/machineStatusRoutes.js +10 -0
- package/dist/server/status/machineStatusRoutes.js.map +1 -0
- package/dist/server/status/machineStatusService.js +115 -0
- package/dist/server/status/machineStatusService.js.map +1 -0
- package/dist/server/status/workspaceAttribution.js +71 -0
- package/dist/server/status/workspaceAttribution.js.map +1 -0
- package/dist/server/terminalProxyRoutes.js.map +1 -1
- package/dist/server/workspaceExplorerRoutes.js +12 -25
- package/dist/server/workspaceExplorerRoutes.js.map +1 -1
- package/dist/server/workspaces/effectivePathAccess.js +3 -2
- package/dist/server/workspaces/effectivePathAccess.js.map +1 -1
- package/dist/server/workspaces/fileContentService.js +17 -9
- package/dist/server/workspaces/fileContentService.js.map +1 -1
- package/dist/server/workspaces/fileOperationService.js +4 -17
- package/dist/server/workspaces/fileOperationService.js.map +1 -1
- package/dist/server/workspaces/filePreviewResponseHeaders.js +18 -0
- package/dist/server/workspaces/filePreviewResponseHeaders.js.map +1 -0
- package/dist/server/workspaces/filePreviewResponsePolicy.js +55 -0
- package/dist/server/workspaces/filePreviewResponsePolicy.js.map +1 -0
- package/dist/server/workspaces/filePreviewService.js +81 -0
- package/dist/server/workspaces/filePreviewService.js.map +1 -0
- package/dist/server/workspaces/projectWorkspaceCwds.js +2 -2
- package/dist/server/workspaces/projectWorkspaceCwds.js.map +1 -1
- package/dist/server/workspaces/sessionDaemonWorkspaceCatalog.js +416 -0
- package/dist/server/workspaces/sessionDaemonWorkspaceCatalog.js.map +1 -0
- package/dist/server/workspaces/workspaceCatalog.js +87 -0
- package/dist/server/workspaces/workspaceCatalog.js.map +1 -0
- package/dist/server/workspaces/workspaceContext.js +5 -5
- package/dist/server/workspaces/workspaceContext.js.map +1 -1
- package/dist/server/workspaces/workspaceDeletionRoutes.js +114 -99
- package/dist/server/workspaces/workspaceDeletionRoutes.js.map +1 -1
- package/dist/server/workspaces/workspaceProviderRegistry.js +650 -0
- package/dist/server/workspaces/workspaceProviderRegistry.js.map +1 -0
- package/dist/server/workspaces/workspaceRemovalService.js +258 -0
- package/dist/server/workspaces/workspaceRemovalService.js.map +1 -0
- package/dist/server/workspaces/workspaceRouteContext.js +2 -1
- package/dist/server/workspaces/workspaceRouteContext.js.map +1 -1
- package/dist/server/workspaces/workspaceRouteErrors.js +7 -0
- package/dist/server/workspaces/workspaceRouteErrors.js.map +1 -0
- package/dist/server/workspaces/worktreePreRemoveHook.js +39 -0
- package/dist/server/workspaces/worktreePreRemoveHook.js.map +1 -0
- package/dist/server-plugin-api.d.ts +115 -0
- package/dist/server-plugin-api.js +2 -0
- package/dist/server-plugin-api.js.map +1 -0
- package/dist/serverPluginRecovery.js +138 -0
- package/dist/serverPluginRecovery.js.map +1 -0
- package/dist/sessiond/config.js +6 -0
- package/dist/sessiond/config.js.map +1 -1
- package/dist/sessiond/sessionDaemonClient.js +7 -6
- package/dist/sessiond/sessionDaemonClient.js.map +1 -1
- package/dist/shared/apiTypes.js +2 -0
- package/dist/shared/apiTypes.js.map +1 -1
- package/dist/shared/capabilities.js +2 -0
- package/dist/shared/capabilities.js.map +1 -1
- package/dist/shared/federatedRoutes.js +28 -2
- package/dist/shared/federatedRoutes.js.map +1 -1
- package/dist/shared/machineStatus.js +60 -0
- package/dist/shared/machineStatus.js.map +1 -0
- package/dist/shared/pluginApiTypes.d.ts +155 -0
- package/dist/shared/pluginBackendProtocol.js +110 -0
- package/dist/shared/pluginBackendProtocol.js.map +1 -0
- package/dist/shared/pluginIds.js +3 -0
- package/dist/shared/pluginIds.js.map +1 -1
- package/dist/shared/pluginRecoveryCommands.js +14 -0
- package/dist/shared/pluginRecoveryCommands.js.map +1 -0
- package/dist/shared/workspaceFiles.js +41 -2
- package/dist/shared/workspaceFiles.js.map +1 -1
- package/dist/shared/workspaceRemovalProtocol.js +24 -0
- package/dist/shared/workspaceRemovalProtocol.js.map +1 -0
- package/docs/config.md +13 -6
- package/docs/plugins.md +15 -21
- package/package.json +26 -8
- package/server-plugin-api.d.ts +1 -0
- package/dist/client/assets/UnifiedDiffViewer-ClTQ6Ok0.js +0 -39
- package/dist/plugin-api/unstable.d.ts +0 -22
- package/dist/server/sessions/parentSessionLocator.js +0 -75
- package/dist/server/sessions/parentSessionLocator.js.map +0 -1
- package/dist/server/workspaces/imagePreviewService.js +0 -40
- package/dist/server/workspaces/imagePreviewService.js.map +0 -1
- package/dist/shared/apiTypes.d.ts +0 -1323
- package/dist/shared/thinkingLevels.d.ts +0 -29
- package/plugin-api/unstable.d.ts +0 -1
|
@@ -1,8 +1,10 @@
|
|
|
1
1
|
import { readdir } from "node:fs/promises";
|
|
2
2
|
import { homedir } from "node:os";
|
|
3
|
-
import { dirname, isAbsolute, join, resolve } from "node:path";
|
|
3
|
+
import { basename, dirname, isAbsolute, join, resolve } from "node:path";
|
|
4
4
|
import { SessionManager, SettingsManager } from "@earendil-works/pi-coding-agent";
|
|
5
5
|
import { canonicalizeStoredCwd, cwdPathsEqual } from "../workingDirectory.js";
|
|
6
|
+
import { readSessionHeaderSummary } from "./sessionFileHeader.js";
|
|
7
|
+
import { SessionSummaryScanner } from "./sessionSummaryScanner.js";
|
|
6
8
|
export class SessionDirResolver {
|
|
7
9
|
constructor(options) {
|
|
8
10
|
this.agentDir = options.agentDir;
|
|
@@ -38,10 +40,36 @@ export function createPiSessionManagerGateway(options) {
|
|
|
38
40
|
class SettingsAwarePiSessionManagerGateway {
|
|
39
41
|
constructor(resolver) {
|
|
40
42
|
this.resolver = resolver;
|
|
43
|
+
/**
|
|
44
|
+
* One memoized scanner per gateway: its per-file summary memo lives as long
|
|
45
|
+
* as the daemon, so repeated listings answer unchanged files from one stat
|
|
46
|
+
* and re-scan changed ones whole. Invalidation is automatic (file identity +
|
|
47
|
+
* size; see SessionSummaryScanner), with `invalidateSessionFile` for the
|
|
48
|
+
* in-place rewrites those checks cannot see.
|
|
49
|
+
*/
|
|
50
|
+
this.summaryScanner = new SessionSummaryScanner();
|
|
41
51
|
}
|
|
42
52
|
async list(cwd) {
|
|
43
53
|
const resolution = this.resolver.resolve(cwd);
|
|
44
|
-
|
|
54
|
+
// Lightweight streaming summaries instead of the SDK's full-transcript
|
|
55
|
+
// listing: same fields, but message bodies are never parsed once the first
|
|
56
|
+
// user message is found. The cross-project cleanup listing (listAll) keeps
|
|
57
|
+
// the SDK path.
|
|
58
|
+
const sessions = (await this.summaryScanner.scanSessionSummariesInDir(resolution.sessionDir)).map((session) => ({
|
|
59
|
+
...session,
|
|
60
|
+
cwd: canonicalizeStoredCwd(session.cwd),
|
|
61
|
+
}));
|
|
62
|
+
return filterSessionsForCwd(sessions, cwd);
|
|
63
|
+
}
|
|
64
|
+
resolveSessionFile(cwd, sessionId) {
|
|
65
|
+
const resolution = this.resolver.resolve(cwd);
|
|
66
|
+
return resolveSessionFileInDir(resolution.sessionDir, cwd, sessionId, readSessionHeaderSummary);
|
|
67
|
+
}
|
|
68
|
+
invalidateSessionFile(sessionFile) {
|
|
69
|
+
// Detach is the only flow that rewrites a session file in place (keeping
|
|
70
|
+
// the inode), and the summary memo cannot detect such rewrites from
|
|
71
|
+
// identity + size alone. Drop the entry so the next listing re-reads it.
|
|
72
|
+
this.summaryScanner.invalidate(sessionFile);
|
|
45
73
|
}
|
|
46
74
|
create(cwd, options) {
|
|
47
75
|
const resolution = this.resolver.resolve(cwd);
|
|
@@ -85,6 +113,105 @@ export function filterSessionsForCwd(sessions, cwd) {
|
|
|
85
113
|
// resolve to this process's cwd and produce false matches.
|
|
86
114
|
return sessions.filter((session) => session.cwd !== "" && cwdPathsEqual(session.cwd, cwd));
|
|
87
115
|
}
|
|
116
|
+
/**
|
|
117
|
+
* Locate a session file by id without parsing transcripts.
|
|
118
|
+
*
|
|
119
|
+
* Session filenames embed the session id (`<timestamp>_<sessionId>.jsonl`), so
|
|
120
|
+
* exact and prefix matches are normally found from the directory names alone
|
|
121
|
+
* and confirmed by one header read. Filename candidates that fail header
|
|
122
|
+
* verification (a copy whose header holds a different session) do not end the
|
|
123
|
+
* search: the remaining files are checked too, so sessions in renamed or
|
|
124
|
+
* hand-named files still resolve. Headers decide: a file whose header id or cwd
|
|
125
|
+
* does not match is not the session.
|
|
126
|
+
*
|
|
127
|
+
* Among matches, an exact header id wins over a prefix match wherever it
|
|
128
|
+
* appears. Ambiguous prefix candidates are considered in two buckets: filename
|
|
129
|
+
* matches before remaining files, with each bucket sorted by the creation time
|
|
130
|
+
* embedded in SDK-style names (see `byNewestEmbeddedTimestamp`).
|
|
131
|
+
*
|
|
132
|
+
* The header's cwd is returned canonicalized, matching what `list` reports.
|
|
133
|
+
*/
|
|
134
|
+
export async function resolveSessionFileInDir(sessionDir, cwd, sessionId, readHeader) {
|
|
135
|
+
const sessionFiles = await listSessionFiles(sessionDir);
|
|
136
|
+
const fileNameMatches = [];
|
|
137
|
+
const remainingFiles = [];
|
|
138
|
+
for (const sessionFile of sessionFiles) {
|
|
139
|
+
(fileNameMatchesSessionId(basename(sessionFile), sessionId) ? fileNameMatches : remainingFiles).push(sessionFile);
|
|
140
|
+
}
|
|
141
|
+
fileNameMatches.sort(byNewestEmbeddedTimestamp);
|
|
142
|
+
remainingFiles.sort(byNewestEmbeddedTimestamp);
|
|
143
|
+
// A prefix match is only provisional: an exact header id wins over it
|
|
144
|
+
// wherever it appears, so the search continues after one is found.
|
|
145
|
+
let prefixMatch;
|
|
146
|
+
// Filename matches first; the remaining files follow so a renamed file still
|
|
147
|
+
// resolves when every filename candidate fails header verification.
|
|
148
|
+
for (const sessionFile of [...fileNameMatches, ...remainingFiles]) {
|
|
149
|
+
const header = await readHeader(sessionFile);
|
|
150
|
+
if (header?.cwd === undefined)
|
|
151
|
+
continue;
|
|
152
|
+
if (!cwdPathsEqual(header.cwd, cwd))
|
|
153
|
+
continue;
|
|
154
|
+
if (header.id === sessionId) {
|
|
155
|
+
return { id: header.id, cwd: canonicalizeStoredCwd(header.cwd), path: sessionFile };
|
|
156
|
+
}
|
|
157
|
+
if (prefixMatch === undefined && header.id.startsWith(sessionId)) {
|
|
158
|
+
prefixMatch = { id: header.id, cwd: canonicalizeStoredCwd(header.cwd), path: sessionFile };
|
|
159
|
+
}
|
|
160
|
+
}
|
|
161
|
+
return prefixMatch;
|
|
162
|
+
}
|
|
163
|
+
/**
|
|
164
|
+
* Order session files newest-first by the timestamp embedded in SDK-style names
|
|
165
|
+
* (`<timestamp>_<sessionId>.jsonl`; the SDK stamps the session's creation time
|
|
166
|
+
* into the name, so this is creation-time order). Files sharing a timestamp,
|
|
167
|
+
* or without one, fall back to a plain code-unit path comparison — never
|
|
168
|
+
* `localeCompare`, whose result varies by locale — so the order is
|
|
169
|
+
* deterministic everywhere.
|
|
170
|
+
*
|
|
171
|
+
* This deliberately drifts from the listing, which orders by modified time:
|
|
172
|
+
* the resolver never stats transcript files, so ambiguous prefix candidates
|
|
173
|
+
* within each bucket are considered in this creation-time order instead.
|
|
174
|
+
*/
|
|
175
|
+
function byNewestEmbeddedTimestamp(a, b) {
|
|
176
|
+
const timestampA = embeddedFileNameTimestamp(basename(a));
|
|
177
|
+
const timestampB = embeddedFileNameTimestamp(basename(b));
|
|
178
|
+
if (timestampA !== timestampB)
|
|
179
|
+
return timestampA < timestampB ? 1 : -1;
|
|
180
|
+
return a < b ? 1 : a > b ? -1 : 0;
|
|
181
|
+
}
|
|
182
|
+
/** The creation timestamp embedded in an SDK-style session file name ("" when absent). */
|
|
183
|
+
function embeddedFileNameTimestamp(fileName) {
|
|
184
|
+
const stem = fileName.slice(0, -".jsonl".length);
|
|
185
|
+
const separatorIndex = stem.indexOf("_");
|
|
186
|
+
return separatorIndex === -1 ? "" : stem.slice(0, separatorIndex);
|
|
187
|
+
}
|
|
188
|
+
/** Whether a `.jsonl` file name embeds `sessionId` (exactly or as a prefix). */
|
|
189
|
+
function fileNameMatchesSessionId(fileName, sessionId) {
|
|
190
|
+
const embeddedId = embeddedFileNameSessionId(fileName);
|
|
191
|
+
return embeddedId !== undefined && (embeddedId === sessionId || embeddedId.startsWith(sessionId));
|
|
192
|
+
}
|
|
193
|
+
/**
|
|
194
|
+
* The session id embedded in an SDK-style session file name
|
|
195
|
+
* (`<timestamp>_<sessionId>.jsonl`). The timestamp never contains `_`, so the
|
|
196
|
+
* id is everything after the first separator; files without one fall back to
|
|
197
|
+
* the whole stem, and header verification discards false guesses.
|
|
198
|
+
*/
|
|
199
|
+
function embeddedFileNameSessionId(fileName) {
|
|
200
|
+
const stem = fileName.slice(0, -".jsonl".length);
|
|
201
|
+
const separatorIndex = stem.indexOf("_");
|
|
202
|
+
const candidate = separatorIndex === -1 ? stem : stem.slice(separatorIndex + 1);
|
|
203
|
+
return candidate === "" ? undefined : candidate;
|
|
204
|
+
}
|
|
205
|
+
async function listSessionFiles(sessionDir) {
|
|
206
|
+
try {
|
|
207
|
+
const names = await readdir(sessionDir);
|
|
208
|
+
return names.filter((name) => name.endsWith(".jsonl")).map((name) => join(sessionDir, name));
|
|
209
|
+
}
|
|
210
|
+
catch {
|
|
211
|
+
// Matches the SDK listing behavior: an unreadable directory lists nothing.
|
|
212
|
+
return [];
|
|
213
|
+
}
|
|
214
|
+
}
|
|
88
215
|
function uniqueSessionsByPath(sessions) {
|
|
89
216
|
const byPath = new Map();
|
|
90
217
|
for (const session of sessions)
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"piSessionManagerGateway.js","sourceRoot":"","sources":["../../../src/server/sessions/piSessionManagerGateway.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,OAAO,EAAE,MAAM,kBAAkB,CAAC;AAC3C,OAAO,EAAE,OAAO,EAAE,MAAM,SAAS,CAAC;AAClC,OAAO,EAAE,OAAO,EAAE,UAAU,EAAE,IAAI,EAAE,OAAO,EAAE,MAAM,WAAW,CAAC;
|
|
1
|
+
{"version":3,"file":"piSessionManagerGateway.js","sourceRoot":"","sources":["../../../src/server/sessions/piSessionManagerGateway.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,OAAO,EAAE,MAAM,kBAAkB,CAAC;AAC3C,OAAO,EAAE,OAAO,EAAE,MAAM,SAAS,CAAC;AAClC,OAAO,EAAE,QAAQ,EAAE,OAAO,EAAE,UAAU,EAAE,IAAI,EAAE,OAAO,EAAE,MAAM,WAAW,CAAC;AACzE,OAAO,EAAE,cAAc,EAAE,eAAe,EAAE,MAAM,iCAAiC,CAAC;AAClF,OAAO,EAAE,qBAAqB,EAAE,aAAa,EAAE,MAAM,wBAAwB,CAAC;AAC9E,OAAO,EAAE,wBAAwB,EAA4B,MAAM,wBAAwB,CAAC;AAC5F,OAAO,EAAE,qBAAqB,EAAE,MAAM,4BAA4B,CAAC;AAiBnE,MAAM,OAAO,kBAAkB;IAK7B,YAAY,OAAkC;QAC5C,IAAI,CAAC,QAAQ,GAAG,OAAO,CAAC,QAAQ,CAAC;QACjC,IAAI,CAAC,aAAa,GAAG,OAAO,CAAC,iBAAiB;aAC3C,GAAG,CAAC,CAAC,GAAG,EAAE,EAAE,CAAC,OAAO,CAAC,GAAG,CAAC,GAAG,CAAC,CAAC;aAC9B,IAAI,CAAC,CAAC,KAAK,EAAE,EAAE,CAAC,KAAK,KAAK,SAAS,IAAI,KAAK,KAAK,EAAE,CAAC,CAAC;QACxD,MAAM,cAAc,GAAG,OAAO,CAAC,GAAG,CAAC,MAAM,CAAC,CAAC;QAC3C,IAAI,CAAC,OAAO,GAAG,cAAc,KAAK,SAAS,IAAI,cAAc,KAAK,EAAE,IAAI,UAAU,CAAC,cAAc,CAAC,CAAC,CAAC,CAAC,cAAc,CAAC,CAAC,CAAC,OAAO,EAAE,CAAC;IAClI,CAAC;IAED,mBAAmB;QACjB,OAAO,qBAAqB,CAAC,IAAI,CAAC,QAAQ,CAAC,CAAC;IAC9C,CAAC;IAED,mBAAmB;QACjB,IAAI,IAAI,CAAC,aAAa,KAAK,SAAS;YAAE,OAAO,SAAS,CAAC;QACvD,MAAM,QAAQ,GAAG,eAAe,CAAC,IAAI,CAAC,aAAa,EAAE,IAAI,CAAC,OAAO,CAAC,CAAC;QACnE,OAAO,UAAU,CAAC,QAAQ,CAAC,CAAC,CAAC,CAAC,QAAQ,CAAC,CAAC,CAAC,SAAS,CAAC;IACrD,CAAC;IAED,OAAO,CAAC,GAAW;QACjB,IAAI,IAAI,CAAC,aAAa,KAAK,SAAS,EAAE,CAAC;YACrC,OAAO,EAAE,MAAM,EAAE,KAAK,EAAE,UAAU,EAAE,qBAAqB,CAAC,IAAI,CAAC,aAAa,EAAE,GAAG,EAAE,IAAI,CAAC,OAAO,CAAC,EAAE,wBAAwB,EAAE,IAAI,EAAE,CAAC;QACrI,CAAC;QAED,MAAM,kBAAkB,GAAG,eAAe,CAAC,MAAM,CAAC,GAAG,EAAE,IAAI,CAAC,QAAQ,CAAC,CAAC,aAAa,EAAE,CAAC;QACtF,IAAI,kBAAkB,KAAK,SAAS,IAAI,kBAAkB,KAAK,EAAE,EAAE,CAAC;YAClE,OAAO,EAAE,MAAM,EAAE,UAAU,EAAE,UAAU,EAAE,qBAAqB,CAAC,kBAAkB,EAAE,GAAG,EAAE,IAAI,CAAC,OAAO,CAAC,EAAE,wBAAwB,EAAE,IAAI,EAAE,CAAC;QAC1I,CAAC;QAED,OAAO,EAAE,MAAM,EAAE,YAAY,EAAE,UAAU,EAAE,mBAAmB,CAAC,GAAG,EAAE,IAAI,CAAC,QAAQ,CAAC,EAAE,wBAAwB,EAAE,KAAK,EAAE,CAAC;IACxH,CAAC;CACF;AAID,MAAM,UAAU,6BAA6B,CAAC,OAAuC;IACnF,OAAO,IAAI,oCAAoC,CAAC,IAAI,kBAAkB,CAAC,OAAO,CAAC,CAAC,CAAC;AACnF,CAAC;AAED,MAAM,oCAAoC;IAUxC,YAA6B,QAA4B;QAA5B,aAAQ,GAAR,QAAQ,CAAoB;QATzD;;;;;;WAMG;QACc,mBAAc,GAAG,IAAI,qBAAqB,EAAE,CAAC;IAEF,CAAC;IAE7D,KAAK,CAAC,IAAI,CAAC,GAAW;QACpB,MAAM,UAAU,GAAG,IAAI,CAAC,QAAQ,CAAC,OAAO,CAAC,GAAG,CAAC,CAAC;QAC9C,uEAAuE;QACvE,2EAA2E;QAC3E,2EAA2E;QAC3E,gBAAgB;QAChB,MAAM,QAAQ,GAAG,CAAC,MAAM,IAAI,CAAC,cAAc,CAAC,yBAAyB,CAAC,UAAU,CAAC,UAAU,CAAC,CAAC,CAAC,GAAG,CAAC,CAAC,OAAO,EAAE,EAAE,CAAC,CAAC;YAC9G,GAAG,OAAO;YACV,GAAG,EAAE,qBAAqB,CAAC,OAAO,CAAC,GAAG,CAAC;SACxC,CAAC,CAAC,CAAC;QACJ,OAAO,oBAAoB,CAAC,QAAQ,EAAE,GAAG,CAAC,CAAC;IAC7C,CAAC;IAED,kBAAkB,CAAC,GAAW,EAAE,SAAiB;QAC/C,MAAM,UAAU,GAAG,IAAI,CAAC,QAAQ,CAAC,OAAO,CAAC,GAAG,CAAC,CAAC;QAC9C,OAAO,uBAAuB,CAAC,UAAU,CAAC,UAAU,EAAE,GAAG,EAAE,SAAS,EAAE,wBAAwB,CAAC,CAAC;IAClG,CAAC;IAED,qBAAqB,CAAC,WAAmB;QACvC,yEAAyE;QACzE,oEAAoE;QACpE,yEAAyE;QACzE,IAAI,CAAC,cAAc,CAAC,UAAU,CAAC,WAAW,CAAC,CAAC;IAC9C,CAAC;IAED,MAAM,CAAC,GAAW,EAAE,OAAoC;QACtD,MAAM,UAAU,GAAG,IAAI,CAAC,QAAQ,CAAC,OAAO,CAAC,GAAG,CAAC,CAAC;QAC9C,OAAO,cAAc,CAAC,MAAM,CAAC,GAAG,EAAE,UAAU,CAAC,UAAU,EAAE,OAAO,EAAE,aAAa,KAAK,SAAS,CAAC,CAAC,CAAC,SAAS,CAAC,CAAC,CAAC,EAAE,aAAa,EAAE,OAAO,CAAC,aAAa,EAAE,CAAC,CAAC;IACxJ,CAAC;IAED,KAAK,CAAC,OAAO;QACX,MAAM,aAAa,GAAG,IAAI,CAAC,QAAQ,CAAC,mBAAmB,EAAE,CAAC;QAC1D,MAAM,CAAC,eAAe,EAAE,WAAW,CAAC,GAAG,MAAM,OAAO,CAAC,GAAG,CAAC;YACvD,4BAA4B,CAAC,IAAI,CAAC,QAAQ,CAAC,mBAAmB,EAAE,CAAC;YACjE,aAAa,KAAK,SAAS,CAAC,CAAC,CAAC,OAAO,CAAC,OAAO,CAAC,EAAE,CAAC,CAAC,CAAC,CAAC,iBAAiB,CAAC,aAAa,CAAC;SACrF,CAAC,CAAC;QACH,OAAO,oBAAoB,CAAC,CAAC,GAAG,eAAe,EAAE,GAAG,WAAW,CAAC,CAAC,CAAC;IACpE,CAAC;IAED,IAAI,CAAC,IAAY;QACf,OAAO,cAAc,CAAC,IAAI,CAAC,IAAI,EAAE,OAAO,CAAC,IAAI,CAAC,CAAC,CAAC;IAClD,CAAC;CACF;AAED,MAAM,CAAC,KAAK,UAAU,iBAAiB,CAAC,UAAkB;IACxD,+EAA+E;IAC/E,kFAAkF;IAClF,4EAA4E;IAC5E,8EAA8E;IAC9E,8DAA8D;IAC9D,MAAM,QAAQ,GAAG,MAAM,cAAc,CAAC,OAAO,CAAC,UAAU,CAAC,CAAC;IAC1D,OAAO,QAAQ,CAAC,GAAG,CAAC,CAAC,OAAO,EAAE,EAAE,CAAC,CAAC,EAAE,GAAG,OAAO,EAAE,GAAG,EAAE,qBAAqB,CAAC,OAAO,CAAC,GAAG,CAAC,EAAE,CAAC,CAAC,CAAC;AAC9F,CAAC;AAED,MAAM,CAAC,KAAK,UAAU,4BAA4B,CAAC,SAAiB;IAClE,IAAI,OAAiB,CAAC;IACtB,IAAI,CAAC;QACH,OAAO,GAAG,MAAM,OAAO,CAAC,SAAS,EAAE,EAAE,aAAa,EAAE,IAAI,EAAE,CAAC,CAAC;IAC9D,CAAC;IAAC,MAAM,CAAC;QACP,OAAO,EAAE,CAAC;IACZ,CAAC;IAED,MAAM,WAAW,GAAG,OAAO,CAAC,MAAM,CAAC,CAAC,KAAK,EAAE,EAAE,CAAC,KAAK,CAAC,WAAW,EAAE,CAAC,CAAC,GAAG,CAAC,CAAC,KAAK,EAAE,EAAE,CAAC,IAAI,CAAC,SAAS,EAAE,KAAK,CAAC,IAAI,CAAC,CAAC,CAAC;IAC/G,MAAM,QAAQ,GAAG,CAAC,MAAM,OAAO,CAAC,GAAG,CAAC,WAAW,CAAC,GAAG,CAAC,CAAC,GAAG,EAAE,EAAE,CAAC,iBAAiB,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,CAAC,IAAI,EAAE,CAAC;IAC9F,OAAO,QAAQ,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,QAAQ,CAAC,OAAO,EAAE,GAAG,CAAC,CAAC,QAAQ,CAAC,OAAO,EAAE,CAAC,CAAC;AAC9E,CAAC;AAED,MAAM,UAAU,oBAAoB,CAAC,QAAuC,EAAE,GAAW;IACvF,iFAAiF;IACjF,2DAA2D;IAC3D,OAAO,QAAQ,CAAC,MAAM,CAAC,CAAC,OAAO,EAAE,EAAE,CAAC,OAAO,CAAC,GAAG,KAAK,EAAE,IAAI,aAAa,CAAC,OAAO,CAAC,GAAG,EAAE,GAAG,CAAC,CAAC,CAAC;AAC7F,CAAC;AAED;;;;;;;;;;;;;;;;;GAiBG;AACH,MAAM,CAAC,KAAK,UAAU,uBAAuB,CAC3C,UAAkB,EAClB,GAAW,EACX,SAAiB,EACjB,UAA+B;IAE/B,MAAM,YAAY,GAAG,MAAM,gBAAgB,CAAC,UAAU,CAAC,CAAC;IACxD,MAAM,eAAe,GAAa,EAAE,CAAC;IACrC,MAAM,cAAc,GAAa,EAAE,CAAC;IACpC,KAAK,MAAM,WAAW,IAAI,YAAY,EAAE,CAAC;QACvC,CAAC,wBAAwB,CAAC,QAAQ,CAAC,WAAW,CAAC,EAAE,SAAS,CAAC,CAAC,CAAC,CAAC,eAAe,CAAC,CAAC,CAAC,cAAc,CAAC,CAAC,IAAI,CAAC,WAAW,CAAC,CAAC;IACpH,CAAC;IACD,eAAe,CAAC,IAAI,CAAC,yBAAyB,CAAC,CAAC;IAChD,cAAc,CAAC,IAAI,CAAC,yBAAyB,CAAC,CAAC;IAE/C,sEAAsE;IACtE,mEAAmE;IACnE,IAAI,WAA4C,CAAC;IACjD,6EAA6E;IAC7E,oEAAoE;IACpE,KAAK,MAAM,WAAW,IAAI,CAAC,GAAG,eAAe,EAAE,GAAG,cAAc,CAAC,EAAE,CAAC;QAClE,MAAM,MAAM,GAAG,MAAM,UAAU,CAAC,WAAW,CAAC,CAAC;QAC7C,IAAI,MAAM,EAAE,GAAG,KAAK,SAAS;YAAE,SAAS;QACxC,IAAI,CAAC,aAAa,CAAC,MAAM,CAAC,GAAG,EAAE,GAAG,CAAC;YAAE,SAAS;QAC9C,IAAI,MAAM,CAAC,EAAE,KAAK,SAAS,EAAE,CAAC;YAC5B,OAAO,EAAE,EAAE,EAAE,MAAM,CAAC,EAAE,EAAE,GAAG,EAAE,qBAAqB,CAAC,MAAM,CAAC,GAAG,CAAC,EAAE,IAAI,EAAE,WAAW,EAAE,CAAC;QACtF,CAAC;QACD,IAAI,WAAW,KAAK,SAAS,IAAI,MAAM,CAAC,EAAE,CAAC,UAAU,CAAC,SAAS,CAAC,EAAE,CAAC;YACjE,WAAW,GAAG,EAAE,EAAE,EAAE,MAAM,CAAC,EAAE,EAAE,GAAG,EAAE,qBAAqB,CAAC,MAAM,CAAC,GAAG,CAAC,EAAE,IAAI,EAAE,WAAW,EAAE,CAAC;QAC7F,CAAC;IACH,CAAC;IACD,OAAO,WAAW,CAAC;AACrB,CAAC;AAED;;;;;;;;;;;GAWG;AACH,SAAS,yBAAyB,CAAC,CAAS,EAAE,CAAS;IACrD,MAAM,UAAU,GAAG,yBAAyB,CAAC,QAAQ,CAAC,CAAC,CAAC,CAAC,CAAC;IAC1D,MAAM,UAAU,GAAG,yBAAyB,CAAC,QAAQ,CAAC,CAAC,CAAC,CAAC,CAAC;IAC1D,IAAI,UAAU,KAAK,UAAU;QAAE,OAAO,UAAU,GAAG,UAAU,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC;IACvE,OAAO,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC;AACpC,CAAC;AAED,0FAA0F;AAC1F,SAAS,yBAAyB,CAAC,QAAgB;IACjD,MAAM,IAAI,GAAG,QAAQ,CAAC,KAAK,CAAC,CAAC,EAAE,CAAC,QAAQ,CAAC,MAAM,CAAC,CAAC;IACjD,MAAM,cAAc,GAAG,IAAI,CAAC,OAAO,CAAC,GAAG,CAAC,CAAC;IACzC,OAAO,cAAc,KAAK,CAAC,CAAC,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC,EAAE,cAAc,CAAC,CAAC;AACpE,CAAC;AAED,gFAAgF;AAChF,SAAS,wBAAwB,CAAC,QAAgB,EAAE,SAAiB;IACnE,MAAM,UAAU,GAAG,yBAAyB,CAAC,QAAQ,CAAC,CAAC;IACvD,OAAO,UAAU,KAAK,SAAS,IAAI,CAAC,UAAU,KAAK,SAAS,IAAI,UAAU,CAAC,UAAU,CAAC,SAAS,CAAC,CAAC,CAAC;AACpG,CAAC;AAED;;;;;GAKG;AACH,SAAS,yBAAyB,CAAC,QAAgB;IACjD,MAAM,IAAI,GAAG,QAAQ,CAAC,KAAK,CAAC,CAAC,EAAE,CAAC,QAAQ,CAAC,MAAM,CAAC,CAAC;IACjD,MAAM,cAAc,GAAG,IAAI,CAAC,OAAO,CAAC,GAAG,CAAC,CAAC;IACzC,MAAM,SAAS,GAAG,cAAc,KAAK,CAAC,CAAC,CAAC,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC,IAAI,CAAC,KAAK,CAAC,cAAc,GAAG,CAAC,CAAC,CAAC;IAChF,OAAO,SAAS,KAAK,EAAE,CAAC,CAAC,CAAC,SAAS,CAAC,CAAC,CAAC,SAAS,CAAC;AAClD,CAAC;AAED,KAAK,UAAU,gBAAgB,CAAC,UAAkB;IAChD,IAAI,CAAC;QACH,MAAM,KAAK,GAAG,MAAM,OAAO,CAAC,UAAU,CAAC,CAAC;QACxC,OAAO,KAAK,CAAC,MAAM,CAAC,CAAC,IAAI,EAAE,EAAE,CAAC,IAAI,CAAC,QAAQ,CAAC,QAAQ,CAAC,CAAC,CAAC,GAAG,CAAC,CAAC,IAAI,EAAE,EAAE,CAAC,IAAI,CAAC,UAAU,EAAE,IAAI,CAAC,CAAC,CAAC;IAC/F,CAAC;IAAC,MAAM,CAAC;QACP,2EAA2E;QAC3E,OAAO,EAAE,CAAC;IACZ,CAAC;AACH,CAAC;AAED,SAAS,oBAAoB,CAAC,QAAuC;IACnE,MAAM,MAAM,GAAG,IAAI,GAAG,EAA8B,CAAC;IACrD,KAAK,MAAM,OAAO,IAAI,QAAQ;QAAE,MAAM,CAAC,GAAG,CAAC,OAAO,CAAC,IAAI,EAAE,OAAO,CAAC,CAAC;IAClE,OAAO,CAAC,GAAG,MAAM,CAAC,MAAM,EAAE,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,QAAQ,CAAC,OAAO,EAAE,GAAG,CAAC,CAAC,QAAQ,CAAC,OAAO,EAAE,CAAC,CAAC;AAC1F,CAAC;AAED,MAAM,UAAU,qBAAqB,CAAC,QAAgB;IACpD,OAAO,IAAI,CAAC,QAAQ,EAAE,UAAU,CAAC,CAAC;AACpC,CAAC;AAED,MAAM,UAAU,mBAAmB,CAAC,GAAW,EAAE,QAAgB;IAC/D,OAAO,0BAA0B,CAAC,qBAAqB,CAAC,QAAQ,CAAC,EAAE,GAAG,CAAC,CAAC;AAC1E,CAAC;AAED,MAAM,UAAU,0BAA0B,CAAC,SAAiB,EAAE,GAAW;IACvE,MAAM,QAAQ,GAAG,KAAK,GAAG,CAAC,OAAO,CAAC,SAAS,EAAE,EAAE,CAAC,CAAC,OAAO,CAAC,UAAU,EAAE,GAAG,CAAC,IAAI,CAAC;IAC9E,OAAO,IAAI,CAAC,SAAS,EAAE,QAAQ,CAAC,CAAC;AACnC,CAAC;AAED,MAAM,UAAU,qBAAqB,CAAC,IAAY,EAAE,GAAW,EAAE,OAAe;IAC9E,MAAM,QAAQ,GAAG,eAAe,CAAC,IAAI,EAAE,OAAO,CAAC,CAAC;IAChD,OAAO,UAAU,CAAC,QAAQ,CAAC,CAAC,CAAC,CAAC,QAAQ,CAAC,CAAC,CAAC,OAAO,CAAC,GAAG,EAAE,QAAQ,CAAC,CAAC;AAClE,CAAC;AAED,SAAS,eAAe,CAAC,IAAY,EAAE,OAAe;IACpD,IAAI,IAAI,KAAK,GAAG;QAAE,OAAO,OAAO,CAAC;IACjC,IAAI,IAAI,CAAC,UAAU,CAAC,IAAI,CAAC;QAAE,OAAO,IAAI,CAAC,OAAO,EAAE,IAAI,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC,CAAC;IAC/D,OAAO,IAAI,CAAC;AACd,CAAC"}
|
|
@@ -2,7 +2,7 @@ import { constants, realpathSync, statSync } from "node:fs";
|
|
|
2
2
|
import { copyFile, mkdir, readFile, writeFile } from "node:fs/promises";
|
|
3
3
|
import { basename, dirname, isAbsolute, join, relative, resolve } from "node:path";
|
|
4
4
|
import { fileURLToPath } from "node:url";
|
|
5
|
-
import { createAgentSessionFromServices, createAgentSessionRuntime, createAgentSessionServices, createEditToolDefinition, createFindToolDefinition, createGrepToolDefinition, getAgentDir, createLsToolDefinition, createReadToolDefinition, createWriteToolDefinition, defineTool,
|
|
5
|
+
import { createAgentSessionFromServices, createAgentSessionRuntime, createAgentSessionServices, createEditToolDefinition, createFindToolDefinition, createGrepToolDefinition, getAgentDir, createLsToolDefinition, createReadToolDefinition, createWriteToolDefinition, defineTool, readStoredCredential, SessionManager, } from "@earendil-works/pi-coding-agent";
|
|
6
6
|
import { projectBrowserMessage } from "../browserMessageProjection.js";
|
|
7
7
|
import { pageMessagesAtSafeBoundary } from "./messagePaging.js";
|
|
8
8
|
import { eventScopeFromManagementContext, managementContextKey, NORMAL_SESSION_EVENT_SCOPE } from "../realtime/sessionEventScope.js";
|
|
@@ -20,10 +20,9 @@ import { createPiSessionManagerGateway } from "./piSessionManagerGateway.js";
|
|
|
20
20
|
import { SessionPinStore } from "./sessionPinStore.js";
|
|
21
21
|
import { SessionActivityCoordinator } from "./sessionActivityCoordinator.js";
|
|
22
22
|
import { searchSessionContent } from "./sessionContentSearch.js";
|
|
23
|
+
import { createLocalOnlyModelRuntime } from "./authService.js";
|
|
23
24
|
import { canonicalizeStoredCwd, cwdPathsEqual } from "../workingDirectory.js";
|
|
24
25
|
import { readSessionHeaderSummary } from "./sessionFileHeader.js";
|
|
25
|
-
import { countOutOfListingChildren, locateOutOfListingParents } from "./parentSessionLocator.js";
|
|
26
|
-
import { siblingWorkspaceCwds } from "../workspaces/projectWorkspaceCwds.js";
|
|
27
26
|
import { workbenchAccessHandle } from "../managementEmbed.js";
|
|
28
27
|
import { DEFAULT_EXTENSION_DIALOGS_TIMEOUT_MS, piWebDataDir } from "../../config.js";
|
|
29
28
|
import { createScopedSettingsManager, resolveSettingsScopeDirectory } from "./projectSettingsScope.js";
|
|
@@ -89,6 +88,9 @@ function spawnTargetError(decision) {
|
|
|
89
88
|
return new Error("派生会话不在已注册项目中");
|
|
90
89
|
return new Error(`cwd 必须是此项目的工作区。允许的路径:${decision.allowedCwds.join(", ")}`);
|
|
91
90
|
}
|
|
91
|
+
function subsessionCwdError(spawningCwd, requestedCwd) {
|
|
92
|
+
return new Error(`跟踪子会话只能在父会话的工作区中运行(${spawningCwd}),不能使用 ${requestedCwd}。如需在其它工作区运行,请使用 spawn_session 创建独立会话。`);
|
|
93
|
+
}
|
|
92
94
|
function modelSpecOf(model) {
|
|
93
95
|
return `${model.provider}/${model.id}`;
|
|
94
96
|
}
|
|
@@ -402,7 +404,13 @@ function createRuntimeWithContextAndOneShotSessionOptions(createRuntime, managem
|
|
|
402
404
|
});
|
|
403
405
|
};
|
|
404
406
|
}
|
|
405
|
-
|
|
407
|
+
/** Append PI WEB-owned prompt sections after the operator's Pi prompt files. */
|
|
408
|
+
export function piWebResourceLoaderOptions(appendSystemPromptSections) {
|
|
409
|
+
if (appendSystemPromptSections.length === 0)
|
|
410
|
+
return undefined;
|
|
411
|
+
return { appendSystemPromptOverride: (base) => [...base, ...appendSystemPromptSections] };
|
|
412
|
+
}
|
|
413
|
+
function createDefaultRuntimeFactory(normalModelRuntimeForCwd, managementModelRuntime, resolveSettingsScope, spawn, subsessions, askUser, workbench, logger = noopLogger, managementAudit, managementProjectIdForCwd, appendSystemPromptSections = []) {
|
|
406
414
|
return async ({ cwd, agentDir, sessionManager, sessionStartEvent, managementContext, initialModel, initialThinkingLevel, delegationToolsEnabled = true }) => {
|
|
407
415
|
const scopedModelRuntime = managementContext === undefined ? await normalModelRuntimeForCwd(cwd) : await managementModelRuntime();
|
|
408
416
|
if (managementContext !== undefined) {
|
|
@@ -421,7 +429,14 @@ function createDefaultRuntimeFactory(normalModelRuntimeForCwd, managementModelRu
|
|
|
421
429
|
scopeDirectory: await resolveSettingsScope(cwd, "normal"),
|
|
422
430
|
globalAgentDir: agentDir,
|
|
423
431
|
});
|
|
424
|
-
const
|
|
432
|
+
const resourceLoaderOptions = piWebResourceLoaderOptions(appendSystemPromptSections);
|
|
433
|
+
const services = await createAgentSessionServices({
|
|
434
|
+
cwd,
|
|
435
|
+
agentDir,
|
|
436
|
+
modelRuntime: scopedModelRuntime,
|
|
437
|
+
settingsManager,
|
|
438
|
+
...(resourceLoaderOptions === undefined ? {} : { resourceLoaderOptions }),
|
|
439
|
+
});
|
|
425
440
|
const customTools = createPiWebCustomToolDefinitions(cwd, delegationToolsEnabled, spawn, subsessions, askUser);
|
|
426
441
|
const result = await createAgentSessionFromServices({
|
|
427
442
|
services,
|
|
@@ -592,7 +607,6 @@ export class PiSessionService {
|
|
|
592
607
|
/** Parent id/file identities whose persisted links have already been loaded. */
|
|
593
608
|
this.subsessionHydratedParents = new Set();
|
|
594
609
|
/** Session file path -> its parsed header. Headers are written once, so successful reads are cached. */
|
|
595
|
-
this.sessionHeaderCache = new Map();
|
|
596
610
|
/**
|
|
597
611
|
* Tracked subsession id -> whether a completion notification is armed.
|
|
598
612
|
* Armed when the child starts working; firing on completion disarms it so a
|
|
@@ -605,20 +619,6 @@ export class PiSessionService {
|
|
|
605
619
|
this.pendingUnreadMutations = [];
|
|
606
620
|
this.unreadPublicationFlushRequested = false;
|
|
607
621
|
this.unreadPublicationStopped = false;
|
|
608
|
-
/**
|
|
609
|
-
* Read a session file header, memoized per path. Pi writes the header once at
|
|
610
|
-
* session creation, so a successful read stays valid for the process lifetime;
|
|
611
|
-
* failures are not cached so a session file that appears later is picked up.
|
|
612
|
-
*/
|
|
613
|
-
this.readCachedSessionHeader = async (sessionFile) => {
|
|
614
|
-
const cached = this.sessionHeaderCache.get(sessionFile);
|
|
615
|
-
if (cached !== undefined)
|
|
616
|
-
return cached;
|
|
617
|
-
const header = await readSessionHeaderSummary(sessionFile);
|
|
618
|
-
if (header !== undefined)
|
|
619
|
-
this.sessionHeaderCache.set(sessionFile, header);
|
|
620
|
-
return header;
|
|
621
|
-
};
|
|
622
622
|
this.archiveStore = deps.archiveStore ?? new SessionArchiveStore();
|
|
623
623
|
this.pinStore = deps.pinStore ?? new SessionPinStore(join(deps.dataDir ?? piWebDataDir(), "session-pins.json"));
|
|
624
624
|
this.agentDir = deps.agentDir ?? getAgentDir();
|
|
@@ -631,7 +631,7 @@ export class PiSessionService {
|
|
|
631
631
|
});
|
|
632
632
|
let defaultModelRuntime;
|
|
633
633
|
const resolveDefaultModelRuntime = () => {
|
|
634
|
-
defaultModelRuntime ??= deps.modelRuntime === undefined ?
|
|
634
|
+
defaultModelRuntime ??= deps.modelRuntime === undefined ? createLocalOnlyModelRuntime() : Promise.resolve(deps.modelRuntime);
|
|
635
635
|
return defaultModelRuntime;
|
|
636
636
|
};
|
|
637
637
|
this.normalModelRuntimeForCwd = deps.normalModelRuntimeForCwd ?? resolveDefaultModelRuntime;
|
|
@@ -640,7 +640,6 @@ export class PiSessionService {
|
|
|
640
640
|
? resolveDefaultModelRuntime
|
|
641
641
|
: () => Promise.resolve(managementModelRuntime);
|
|
642
642
|
this.spawnTargets = deps.spawnTargets;
|
|
643
|
-
this.projectWorkspaces = deps.projectWorkspaces;
|
|
644
643
|
this.logger = deps.logger ?? noopLogger;
|
|
645
644
|
this.normalToolAudit = deps.normalToolAudit;
|
|
646
645
|
this.managementAudit = deps.managementAudit;
|
|
@@ -648,13 +647,14 @@ export class PiSessionService {
|
|
|
648
647
|
this.now = deps.now ?? (() => new Date());
|
|
649
648
|
this.notificationStore = deps.notificationStore ?? new SessionNotificationStore();
|
|
650
649
|
this.unreadStore = deps.unreadStore ?? new SessionUnreadStore();
|
|
650
|
+
this.onUnreadChanged = deps.onUnreadChanged;
|
|
651
651
|
this.pendingAskStore = deps.pendingAskStore ?? new PendingAskStore();
|
|
652
652
|
this.pendingExtensionDialogStore = deps.pendingExtensionDialogStore ?? new PendingExtensionDialogStore();
|
|
653
653
|
this.extensionDialogsTimeoutMs = deps.extensionDialogsTimeoutMs ?? DEFAULT_EXTENSION_DIALOGS_TIMEOUT_MS;
|
|
654
654
|
this.catalogRefreshStatus = deps.catalogRefreshStatus;
|
|
655
655
|
this.unreadPublicationRetryInitialMs = Math.max(0, deps.unreadPublicationRetryDelayMs ?? DEFAULT_UNREAD_PUBLICATION_RETRY_MS);
|
|
656
656
|
this.unreadPublicationRetryDelayMs = this.unreadPublicationRetryInitialMs;
|
|
657
|
-
// Subsessions are
|
|
657
|
+
// Subsessions are gated behind their own flag, and they
|
|
658
658
|
// also require the spawn capability (they share its project-scope resolver).
|
|
659
659
|
const subsessionsActive = this.spawnTargets !== undefined && deps.subsessionsEnabled === true;
|
|
660
660
|
this.createRuntime = deps.createRuntime ?? createDefaultRuntimeFactory(this.normalModelRuntimeForCwd, this.managementModelRuntime, (cwd, mode) => this.resolveSettingsScopeDirectory(cwd, mode), this.spawnTargets === undefined ? undefined : (input) => this.spawnSession(input), !subsessionsActive ? undefined : {
|
|
@@ -662,7 +662,7 @@ export class PiSessionService {
|
|
|
662
662
|
list: (parentSessionId, parentSessionFile) => this.listSubsessions(parentSessionId, parentSessionFile),
|
|
663
663
|
check: (parentSessionId, sessionId, parentSessionFile) => this.checkSubsession(parentSessionId, sessionId, parentSessionFile),
|
|
664
664
|
read: (parentSessionId, sessionId, query, parentSessionFile) => this.readSubsession(parentSessionId, sessionId, query, parentSessionFile),
|
|
665
|
-
}, deps.askUserEnabled === true ? { open: (input) => this.openAsk(input) } : undefined, deps.workbench, this.logger, this.managementAudit, this.managementProjectIdForCwd);
|
|
665
|
+
}, deps.askUserEnabled === true ? { open: (input) => this.openAsk(input) } : undefined, deps.workbench, this.logger, this.managementAudit, this.managementProjectIdForCwd, deps.appendSystemPromptSections ?? []);
|
|
666
666
|
this.createAgentRuntime = deps.createAgentRuntime ?? defaultCreateAgentRuntime;
|
|
667
667
|
this.workspaceActivity = deps.workspaceActivity;
|
|
668
668
|
this.heartbeat = setInterval(() => { this.publishHeartbeats(); }, deps.heartbeatIntervalMs ?? 2000);
|
|
@@ -775,7 +775,6 @@ export class PiSessionService {
|
|
|
775
775
|
this.subsessionLinks.clear();
|
|
776
776
|
this.subsessionHydratedParents.clear();
|
|
777
777
|
this.subsessionNotifyArmed.clear();
|
|
778
|
-
this.sessionHeaderCache.clear();
|
|
779
778
|
this.notificationStore.clearAll("service-dispose");
|
|
780
779
|
await Promise.all(activeSessions.map(async (active) => {
|
|
781
780
|
active.unsubscribe();
|
|
@@ -854,67 +853,11 @@ export class PiSessionService {
|
|
|
854
853
|
.map((record) => clientSessionFromArchivedRecord(record, sessionsById.get(record.sessionId)))
|
|
855
854
|
.filter(isDefined);
|
|
856
855
|
return {
|
|
857
|
-
sessions:
|
|
856
|
+
sessions: [...unarchivedSessions, ...archivedSessions],
|
|
858
857
|
entriesById: new Map(sessions.map((session) => [session.id, session])),
|
|
859
858
|
archivedById,
|
|
860
859
|
};
|
|
861
860
|
}
|
|
862
|
-
/**
|
|
863
|
-
* Annotate a listing with the session relationships that cross workspace
|
|
864
|
-
* boundaries: where an out-of-listing parent lives, and how many children a
|
|
865
|
-
* listed session has in sibling workspaces.
|
|
866
|
-
*
|
|
867
|
-
* Both directions are best-effort. An unreadable parent header or a sibling
|
|
868
|
-
* workspace that cannot be listed leaves the session unannotated rather than
|
|
869
|
-
* failing the listing, and the browser falls back to its generic states.
|
|
870
|
-
*/
|
|
871
|
-
async withRelatedSessionsElsewhere(sessions, cwd) {
|
|
872
|
-
const [parentLocations, childCounts] = await Promise.all([
|
|
873
|
-
locateOutOfListingParents(sessions, cwd, this.readCachedSessionHeader),
|
|
874
|
-
this.countChildrenInSiblingWorkspaces(sessions, cwd),
|
|
875
|
-
]);
|
|
876
|
-
return sessions.map((session) => {
|
|
877
|
-
const parent = session.parentSessionPath === undefined ? undefined : parentLocations.get(session.parentSessionPath);
|
|
878
|
-
const childrenElsewhere = childCounts.get(session.path);
|
|
879
|
-
if (parent === undefined && childrenElsewhere === undefined)
|
|
880
|
-
return session;
|
|
881
|
-
const annotated = { ...session };
|
|
882
|
-
if (parent !== undefined) {
|
|
883
|
-
annotated.parentSessionId = parent.parentSessionId;
|
|
884
|
-
annotated.parentSessionCwd = parent.parentSessionCwd;
|
|
885
|
-
}
|
|
886
|
-
if (childrenElsewhere !== undefined)
|
|
887
|
-
annotated.childSessionsElsewhere = childrenElsewhere;
|
|
888
|
-
return annotated;
|
|
889
|
-
});
|
|
890
|
-
}
|
|
891
|
-
/**
|
|
892
|
-
* Count children of the listed sessions that live in other workspaces of the
|
|
893
|
-
* same project.
|
|
894
|
-
*
|
|
895
|
-
* Only sibling workspaces are scanned: agents may only spawn into workspaces
|
|
896
|
-
* of the spawning session's own project, so that bounds where a child can be.
|
|
897
|
-
* Listing is skipped entirely when no project-workspace locator is configured
|
|
898
|
-
* or the cwd belongs to no registered project.
|
|
899
|
-
*/
|
|
900
|
-
async countChildrenInSiblingWorkspaces(sessions, cwd) {
|
|
901
|
-
if (this.projectWorkspaces === undefined || sessions.length === 0)
|
|
902
|
-
return new Map();
|
|
903
|
-
try {
|
|
904
|
-
const siblingCwds = await siblingWorkspaceCwds(this.projectWorkspaces, cwd);
|
|
905
|
-
if (siblingCwds.length === 0)
|
|
906
|
-
return new Map();
|
|
907
|
-
const listings = await Promise.all(siblingCwds.map(async (siblingCwd) => {
|
|
908
|
-
const entries = await this.sessionManager.list(siblingCwd);
|
|
909
|
-
return entries.flatMap((entry) => entry.parentSessionPath === undefined ? [] : [entry.parentSessionPath]);
|
|
910
|
-
}));
|
|
911
|
-
return countOutOfListingChildren(sessions, listings.flat());
|
|
912
|
-
}
|
|
913
|
-
catch (error) {
|
|
914
|
-
this.logger.info({ cwd, error: error instanceof Error ? error.message : String(error) }, "failed to count child sessions in sibling workspaces");
|
|
915
|
-
return new Map();
|
|
916
|
-
}
|
|
917
|
-
}
|
|
918
861
|
async start(cwd, options = {}) {
|
|
919
862
|
if (options.managementContext === undefined)
|
|
920
863
|
await this.normalModelRuntimeForCwd(cwd);
|
|
@@ -956,7 +899,7 @@ export class PiSessionService {
|
|
|
956
899
|
async spawnSession(input) {
|
|
957
900
|
if (this.spawnTargets === undefined)
|
|
958
901
|
throw new Error("派生会话已禁用");
|
|
959
|
-
const decision = await this.spawnTargets.resolveSpawnTarget(input.spawningCwd, input.cwd);
|
|
902
|
+
const decision = await this.spawnTargets.resolveSpawnTarget(input.spawningCwd, input.cwd, eventScopeFromManagementContext(input.managementContext));
|
|
960
903
|
if (!decision.allowed)
|
|
961
904
|
throw spawnTargetError(decision);
|
|
962
905
|
// A model spec overrides the inherited model. Only a spec triggers a
|
|
@@ -981,16 +924,14 @@ export class PiSessionService {
|
|
|
981
924
|
...(modelUsed === undefined ? {} : { model: modelSpecOf(modelUsed) }),
|
|
982
925
|
};
|
|
983
926
|
}
|
|
984
|
-
/**
|
|
985
|
-
* Start a *tracked* child session on behalf of a LLM. Identical to
|
|
986
|
-
* {@link spawnSession} in how the target cwd is resolved, but the child
|
|
987
|
-
* records its parent (so it shows in the session tree) and is registered so
|
|
988
|
-
* the parent is notified when it stops working and can inspect it later.
|
|
989
|
-
*/
|
|
927
|
+
/** Start a tracked child in the parent's workspace so it remains visible in the same session tree. */
|
|
990
928
|
async spawnSubsession(input) {
|
|
991
929
|
if (this.spawnTargets === undefined)
|
|
992
930
|
throw new Error("派生会话已禁用");
|
|
993
|
-
|
|
931
|
+
if (input.cwd !== undefined && input.cwd !== "" && !cwdPathsEqual(input.cwd, input.spawningCwd)) {
|
|
932
|
+
throw subsessionCwdError(input.spawningCwd, input.cwd);
|
|
933
|
+
}
|
|
934
|
+
const decision = await this.spawnTargets.resolveSpawnTarget(input.spawningCwd, undefined, eventScopeFromManagementContext(input.managementContext));
|
|
994
935
|
if (!decision.allowed)
|
|
995
936
|
throw spawnTargetError(decision);
|
|
996
937
|
// A model spec overrides the inherited model and is resolved against the
|
|
@@ -1033,7 +974,9 @@ export class PiSessionService {
|
|
|
1033
974
|
* first so callers see newly configured providers and models.
|
|
1034
975
|
*/
|
|
1035
976
|
async sessionModelCandidates(session) {
|
|
1036
|
-
|
|
977
|
+
// Pi 0.84 keeps request paths local-only; the bounded catalog refresher is
|
|
978
|
+
// the only path allowed to fetch provider catalogs over the network.
|
|
979
|
+
await session.modelRuntime.refresh({ allowNetwork: false });
|
|
1037
980
|
return session.scopedModels.length > 0
|
|
1038
981
|
? session.scopedModels.map((scoped) => scoped.model)
|
|
1039
982
|
: await session.modelRuntime.getAvailable();
|
|
@@ -1084,9 +1027,9 @@ export class PiSessionService {
|
|
|
1084
1027
|
* work (`deliverAs: "followUp"`), which is how the run that `ask_user`
|
|
1085
1028
|
* terminated continues.
|
|
1086
1029
|
*/
|
|
1087
|
-
async submitAsk(ref, askId, submission) {
|
|
1030
|
+
async submitAsk(ref, askId, submission, managementContext) {
|
|
1088
1031
|
await this.assertWritable(ref);
|
|
1089
|
-
const session = await this.getOrOpen(ref);
|
|
1032
|
+
const session = await this.getOrOpen(ref, managementContext);
|
|
1090
1033
|
// Checked before the store closes the ask so a refused delivery cannot
|
|
1091
1034
|
// discard answers the user already submitted.
|
|
1092
1035
|
this.assertTreeNavigationInactive(session, "answer questions");
|
|
@@ -1097,9 +1040,9 @@ export class PiSessionService {
|
|
|
1097
1040
|
* question as unanswered: it was promised a follow-up message and would
|
|
1098
1041
|
* otherwise wait for one that never comes.
|
|
1099
1042
|
*/
|
|
1100
|
-
async cancelAsk(ref, askId) {
|
|
1043
|
+
async cancelAsk(ref, askId, managementContext) {
|
|
1101
1044
|
await this.assertWritable(ref);
|
|
1102
|
-
const session = await this.getOrOpen(ref);
|
|
1045
|
+
const session = await this.getOrOpen(ref, managementContext);
|
|
1103
1046
|
this.assertTreeNavigationInactive(session, "dismiss questions");
|
|
1104
1047
|
return this.closeAsk(session, this.pendingAskStore.cancel(session.sessionId, askId));
|
|
1105
1048
|
}
|
|
@@ -1851,6 +1794,73 @@ export class PiSessionService {
|
|
|
1851
1794
|
}
|
|
1852
1795
|
}
|
|
1853
1796
|
}
|
|
1797
|
+
async forkFromTree(ref, request, managementContext) {
|
|
1798
|
+
if (request.entryId.trim() === "")
|
|
1799
|
+
throw new Error("Session tree entry is required");
|
|
1800
|
+
const requestedSessionId = sessionIdFromLookup(ref);
|
|
1801
|
+
if (this.isTreeExclusiveSessionIdentityActive(requestedSessionId)) {
|
|
1802
|
+
throw new Error("Stop current session activity before forking the session tree");
|
|
1803
|
+
}
|
|
1804
|
+
await this.assertWritable(ref);
|
|
1805
|
+
const session = await this.getOrOpen(ref, managementContext);
|
|
1806
|
+
if (this.hasActiveWork(session))
|
|
1807
|
+
throw new Error("Stop current session activity before forking the session tree");
|
|
1808
|
+
const sourceSessionId = session.sessionId;
|
|
1809
|
+
const sourceEventScope = this.eventScopeForSession(session);
|
|
1810
|
+
const sourceCwd = session.sessionManager.getCwd();
|
|
1811
|
+
this.publishActivity(session, "正在从会话树条目分叉", "active");
|
|
1812
|
+
this.publishStatus(session);
|
|
1813
|
+
try {
|
|
1814
|
+
const result = await this.commandService.forkEntry(session.sessionId, request.entryId, sourceEventScope, {
|
|
1815
|
+
expectedLeafId: request.expectedLeafId,
|
|
1816
|
+
});
|
|
1817
|
+
if (result.type === "unsupported")
|
|
1818
|
+
throw new Error(result.message);
|
|
1819
|
+
if (result.type !== "done")
|
|
1820
|
+
throw new Error("Session fork is unavailable");
|
|
1821
|
+
if (result.session === undefined) {
|
|
1822
|
+
if (this.isCurrentActiveSession(session)) {
|
|
1823
|
+
this.publishActivity(session, "会话分叉已取消", "idle");
|
|
1824
|
+
this.publishStatus(session);
|
|
1825
|
+
}
|
|
1826
|
+
return { cancelled: true };
|
|
1827
|
+
}
|
|
1828
|
+
const forked = this.activeForLookup({ id: result.session.id, cwd: result.session.cwd }, managementContext)?.runtime.session;
|
|
1829
|
+
if (forked !== undefined && this.isCurrentActiveSession(forked)) {
|
|
1830
|
+
this.publishActivity(forked, "会话已分叉", "idle");
|
|
1831
|
+
this.publishStatus(forked);
|
|
1832
|
+
}
|
|
1833
|
+
if (result.session.id !== sourceSessionId)
|
|
1834
|
+
this.clearSupersededSessionActivity(sourceSessionId, sourceCwd, sourceEventScope);
|
|
1835
|
+
return {
|
|
1836
|
+
cancelled: false,
|
|
1837
|
+
session: result.session,
|
|
1838
|
+
...(result.promptDraft === undefined ? {} : { promptDraft: result.promptDraft }),
|
|
1839
|
+
};
|
|
1840
|
+
}
|
|
1841
|
+
catch (error) {
|
|
1842
|
+
const message = error instanceof Error ? error.message : String(error);
|
|
1843
|
+
if (this.isCurrentActiveSession(session)) {
|
|
1844
|
+
this.publishActivity(session, "会话分叉失败", "error", message);
|
|
1845
|
+
this.events.publish(session.sessionId, { type: "session.error", message }, sourceEventScope);
|
|
1846
|
+
this.publishStatus(session);
|
|
1847
|
+
}
|
|
1848
|
+
throw error;
|
|
1849
|
+
}
|
|
1850
|
+
}
|
|
1851
|
+
clearSupersededSessionActivity(sessionId, cwd, eventScope) {
|
|
1852
|
+
const sessionKey = activeSessionKey(sessionId, eventScope);
|
|
1853
|
+
if (this.activities.get(sessionKey)?.phase === "active") {
|
|
1854
|
+
const at = new Date().toISOString();
|
|
1855
|
+
const stored = { phase: "idle", label: "空闲", at };
|
|
1856
|
+
this.activities.set(sessionKey, stored);
|
|
1857
|
+
const activity = { sessionId, ...stored };
|
|
1858
|
+
this.events.publish(sessionId, { type: "activity.update", activity }, eventScope);
|
|
1859
|
+
this.events.publishGlobal({ type: "activity.update", activity }, eventScope);
|
|
1860
|
+
}
|
|
1861
|
+
this.workspaceActivity?.removeSession(sessionId, cwd, eventScope);
|
|
1862
|
+
this.activities.delete(sessionKey);
|
|
1863
|
+
}
|
|
1854
1864
|
async archive(ref, managementContext) {
|
|
1855
1865
|
const active = await this.getActive(ref, managementContext);
|
|
1856
1866
|
const session = active.runtime.session;
|
|
@@ -2090,6 +2100,7 @@ export class PiSessionService {
|
|
|
2090
2100
|
if (sessionFile === undefined || sessionFile === "")
|
|
2091
2101
|
throw new Error("会话尚未持久化");
|
|
2092
2102
|
await clearParentSession(sessionFile);
|
|
2103
|
+
this.sessionManager.invalidateSessionFile?.(sessionFile);
|
|
2093
2104
|
clearParentSessionHeader(session.sessionManager);
|
|
2094
2105
|
this.unregisterSubsession(session.sessionId);
|
|
2095
2106
|
await this.forgetUnreadSessions([{ sessionId: session.sessionId, cwd: session.sessionManager.getCwd() }]);
|
|
@@ -2432,8 +2443,10 @@ export class PiSessionService {
|
|
|
2432
2443
|
return this.openPending(archived.sessionId, archived.cwd, managementContext, () => this.sessionManager.open(archivePath), { notifications: "disabled" });
|
|
2433
2444
|
}
|
|
2434
2445
|
const match = isPiSessionRef(ref)
|
|
2435
|
-
?
|
|
2436
|
-
|
|
2446
|
+
? this.sessionManager.resolveSessionFile === undefined
|
|
2447
|
+
? findSessionByIdOrPrefix(await this.sessionManager.list(ref.cwd), ref.id)
|
|
2448
|
+
: await this.sessionManager.resolveSessionFile(ref.cwd, ref.id)
|
|
2449
|
+
: findSessionByIdOrPrefix(await this.sessionManager.listAll?.() ?? [], ref);
|
|
2437
2450
|
if (!match)
|
|
2438
2451
|
throw new Error("未找到会话");
|
|
2439
2452
|
return this.openPending(match.id, match.cwd, managementContext, () => this.sessionManager.open(match.path));
|
|
@@ -3137,6 +3150,16 @@ export class PiSessionService {
|
|
|
3137
3150
|
await this.publishUnreadMutations([]);
|
|
3138
3151
|
return this.unreadStore.durableCatalogSnapshot();
|
|
3139
3152
|
}
|
|
3153
|
+
/** Scope-filtered unread view used only by the daemon-owned status tree. */
|
|
3154
|
+
async unreadCatalogForScope(eventScope) {
|
|
3155
|
+
const catalog = await this.unreadCatalog();
|
|
3156
|
+
if (eventScope === NORMAL_SESSION_EVENT_SCOPE)
|
|
3157
|
+
return catalog;
|
|
3158
|
+
const visible = new Set([...this.active.values()]
|
|
3159
|
+
.filter((active) => active.eventScope === eventScope)
|
|
3160
|
+
.map((active) => active.runtime.session.sessionId));
|
|
3161
|
+
return { ...catalog, sessions: catalog.sessions.filter((session) => visible.has(session.sessionId)) };
|
|
3162
|
+
}
|
|
3140
3163
|
async acknowledgeUnread(sessionId, request) {
|
|
3141
3164
|
const result = this.unreadStore.acknowledge(sessionId, {
|
|
3142
3165
|
...request,
|
|
@@ -3440,6 +3463,17 @@ export class PiSessionService {
|
|
|
3440
3463
|
void this.publishUnreadMutations(mutations).catch(() => undefined);
|
|
3441
3464
|
}
|
|
3442
3465
|
publishUnreadMutations(mutations) {
|
|
3466
|
+
if (mutations.length > 0) {
|
|
3467
|
+
const scopes = new Set();
|
|
3468
|
+
for (const mutation of mutations) {
|
|
3469
|
+
const activeScopes = this.activeSessionsForId(mutation.event.sessionId).map((active) => active.eventScope);
|
|
3470
|
+
if (activeScopes.length === 0)
|
|
3471
|
+
scopes.add(NORMAL_SESSION_EVENT_SCOPE);
|
|
3472
|
+
else
|
|
3473
|
+
activeScopes.forEach((scope) => scopes.add(scope));
|
|
3474
|
+
}
|
|
3475
|
+
scopes.forEach((scope) => this.onUnreadChanged?.(scope));
|
|
3476
|
+
}
|
|
3443
3477
|
this.enqueueUnreadMutations(mutations);
|
|
3444
3478
|
this.unreadPublicationFlushRequested = true;
|
|
3445
3479
|
if (this.unreadPublication === undefined && this.unreadPublicationRetryTimer !== undefined) {
|