@chainingintention/pi-web-cn 1.202607.5 → 1.202607.7
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 +3 -0
- package/dist/cli.js +283 -315
- package/dist/cli.js.map +1 -1
- package/dist/client/assets/CodeViewer-DxWBEGHy.js +4 -0
- package/dist/client/assets/TerminalPanel-CLBbwrSg.js +184 -0
- package/dist/client/assets/UnifiedDiffViewer-CvVtrOUV.js +39 -0
- package/dist/client/assets/index-CEswQiQV.js +3149 -0
- package/dist/client/assets/vendor-editor-core-vUi74DnD.js +12 -0
- package/dist/client/assets/vendor-editor-languages-DoaXUodB.js +46 -0
- package/dist/client/assets/vendor-editor-legacy-CYBnW6ZU.js +1 -0
- package/dist/client/assets/vendor-terminal-BrP-ENHg.css +1 -0
- package/dist/client/assets/vendor-terminal-D8k4UKM2.js +35 -0
- package/dist/client/index.html +6 -6
- package/dist/client/manifest.webmanifest +4 -4
- package/dist/config.js +221 -9
- package/dist/config.js.map +1 -1
- package/dist/nativeServices/serviceDoctor.js +480 -0
- package/dist/nativeServices/serviceDoctor.js.map +1 -0
- package/dist/nativeServices/serviceInstall.js +35 -0
- package/dist/nativeServices/serviceInstall.js.map +1 -0
- package/dist/nativeServices/servicePlan.js +357 -0
- package/dist/nativeServices/servicePlan.js.map +1 -0
- package/dist/nativeServices/serviceProbe.js +445 -0
- package/dist/nativeServices/serviceProbe.js.map +1 -0
- package/dist/nativeServices/serviceRendering.js +122 -0
- package/dist/nativeServices/serviceRendering.js.map +1 -0
- package/dist/pi-web-plugins/updates/pi-web-plugin.js +11 -0
- package/dist/plugin-api.d.ts +2 -0
- package/dist/server/activeAgentProfileProvider.js +25 -0
- package/dist/server/activeAgentProfileProvider.js.map +1 -0
- package/dist/server/app.js +63 -16
- package/dist/server/app.js.map +1 -1
- package/dist/server/browserMessageProjection.js +56 -0
- package/dist/server/browserMessageProjection.js.map +1 -0
- package/dist/server/configRoutes.js +152 -10
- package/dist/server/configRoutes.js.map +1 -1
- package/dist/server/machines/machineClient.js +19 -9
- package/dist/server/machines/machineClient.js.map +1 -1
- package/dist/server/machines/machinePluginProxyRoutes.js +7 -4
- package/dist/server/machines/machinePluginProxyRoutes.js.map +1 -1
- package/dist/server/machines/machineProxyRoutes.js +16 -4
- package/dist/server/machines/machineProxyRoutes.js.map +1 -1
- package/dist/server/machines/machineRoutes.js +1 -1
- package/dist/server/machines/machineRoutes.js.map +1 -1
- package/dist/server/machines/machineService.js +2 -2
- package/dist/server/machines/machineService.js.map +1 -1
- package/dist/server/managementEmbed.js +28 -11
- package/dist/server/managementEmbed.js.map +1 -1
- package/dist/server/piPackageRoutes.js +7 -3
- package/dist/server/piPackageRoutes.js.map +1 -1
- package/dist/server/piPackageService.js +34 -2
- package/dist/server/piPackageService.js.map +1 -1
- package/dist/server/piWebPluginService.js +48 -12
- package/dist/server/piWebPluginService.js.map +1 -1
- package/dist/server/piWebReleaseLookupCache.js +37 -0
- package/dist/server/piWebReleaseLookupCache.js.map +1 -0
- package/dist/server/piWebStatus.js +48 -49
- package/dist/server/piWebStatus.js.map +1 -1
- package/dist/server/piWebStatusCache.js +19 -6
- package/dist/server/piWebStatusCache.js.map +1 -1
- package/dist/server/realtime/sessionEventHub.js +2 -1
- package/dist/server/realtime/sessionEventHub.js.map +1 -1
- package/dist/server/realtime/sessionEventScope.js +0 -3
- package/dist/server/realtime/sessionEventScope.js.map +1 -1
- package/dist/server/sessiond/sessionDaemonStartup.js +49 -0
- package/dist/server/sessiond/sessionDaemonStartup.js.map +1 -0
- package/dist/server/sessiond.js +113 -70
- package/dist/server/sessiond.js.map +1 -1
- package/dist/server/sessions/authProviderOptions.js +23 -22
- package/dist/server/sessions/authProviderOptions.js.map +1 -1
- package/dist/server/sessions/authRoutes.js +14 -12
- package/dist/server/sessions/authRoutes.js.map +1 -1
- package/dist/server/sessions/authService.js +28 -22
- package/dist/server/sessions/authService.js.map +1 -1
- package/dist/server/sessions/oauthLoginFlowService.js +17 -19
- package/dist/server/sessions/oauthLoginFlowService.js.map +1 -1
- package/dist/server/sessions/piSessionManagerGateway.js +22 -21
- package/dist/server/sessions/piSessionManagerGateway.js.map +1 -1
- package/dist/server/sessions/piSessionService.js +261 -111
- package/dist/server/sessions/piSessionService.js.map +1 -1
- package/dist/server/sessions/projectAuthService.js +135 -0
- package/dist/server/sessions/projectAuthService.js.map +1 -0
- package/dist/server/sessions/projectSettingsScope.js +263 -0
- package/dist/server/sessions/projectSettingsScope.js.map +1 -0
- package/dist/server/sessions/sessionArchiveMigration.js +590 -0
- package/dist/server/sessions/sessionArchiveMigration.js.map +1 -0
- package/dist/server/sessions/sessionArchiveStore.js +7 -4
- package/dist/server/sessions/sessionArchiveStore.js.map +1 -1
- package/dist/server/sessions/sessionRoutes.js +15 -4
- package/dist/server/sessions/sessionRoutes.js.map +1 -1
- package/dist/server/sessions/sessionService.js +2 -0
- package/dist/server/sessions/sessionService.js.map +1 -0
- package/dist/server/sessions/spawnSubsessionTool.js +71 -31
- package/dist/server/sessions/spawnSubsessionTool.js.map +1 -1
- package/dist/server/terminals/terminalService.js +2 -2
- package/dist/server/terminals/terminalService.js.map +1 -1
- package/dist/sessiond/activeAgentProfile.js +31 -0
- package/dist/sessiond/activeAgentProfile.js.map +1 -0
- package/dist/sessiond/sessionDaemonClient.js +38 -0
- package/dist/sessiond/sessionDaemonClient.js.map +1 -1
- package/dist/shared/activeAgentProfile.js +76 -0
- package/dist/shared/activeAgentProfile.js.map +1 -0
- package/dist/shared/apiTypes.d.ts +26 -0
- package/dist/shared/apiTypes.js +2 -0
- package/dist/shared/apiTypes.js.map +1 -1
- package/dist/shared/capabilities.js +5 -0
- package/dist/shared/capabilities.js.map +1 -1
- package/dist/shared/federatedRoutes.js +1 -0
- package/dist/shared/federatedRoutes.js.map +1 -1
- package/dist/shared/piWebConfigParsing.js +0 -5
- package/dist/shared/piWebConfigParsing.js.map +1 -1
- package/dist/shared/piWebStatusParsing.js +6 -0
- package/dist/shared/piWebStatusParsing.js.map +1 -1
- package/dist/shared/thinkingLevels.d.ts +1 -1
- package/dist/shared/thinkingLevels.js +1 -1
- package/dist/shared/thinkingLevels.js.map +1 -1
- package/docs/config.md +63 -9
- package/docs/plugins.md +18 -5
- package/package.json +35 -31
- package/dist/client/assets/CodeViewer-CfZodNYb.js +0 -4
- package/dist/client/assets/TerminalPanel-eua763mA.js +0 -126
- package/dist/client/assets/UnifiedDiffViewer-C9-QeGMj.js +0 -34
- package/dist/client/assets/index-BiFWXoRZ.js +0 -3084
- package/dist/client/assets/vendor-editor-core-C7aWiQy0.js +0 -12
- package/dist/client/assets/vendor-editor-languages-EW34FfVy.js +0 -26
- package/dist/client/assets/vendor-editor-legacy-B4QLsWF8.js +0 -1
- package/dist/client/assets/vendor-terminal-DDGTF8rc.css +0 -1
- package/dist/client/assets/vendor-terminal-DjQ08hXu.js +0 -16
|
@@ -1,19 +1,21 @@
|
|
|
1
1
|
import { statSync } from "node:fs";
|
|
2
2
|
import { open, readFile, writeFile } from "node:fs/promises";
|
|
3
3
|
import { basename, dirname, isAbsolute, relative, resolve } from "node:path";
|
|
4
|
-
import {
|
|
4
|
+
import { createAgentSessionFromServices, createAgentSessionRuntime, createAgentSessionServices, createEditToolDefinition, createFindToolDefinition, createGrepToolDefinition, getAgentDir, createLsToolDefinition, createReadToolDefinition, createWriteToolDefinition, defineTool, ModelRuntime, SessionManager, } from "@earendil-works/pi-coding-agent";
|
|
5
5
|
import { pageMessagesAtSafeBoundary } from "./messagePaging.js";
|
|
6
|
-
import {
|
|
6
|
+
import { eventScopeFromManagementContext, managementContextKey, NORMAL_SESSION_EVENT_SCOPE } from "../realtime/sessionEventScope.js";
|
|
7
7
|
import { BUILTIN_COMMANDS } from "./builtinCommands.js";
|
|
8
8
|
import { SessionCommandService } from "./sessionCommandService.js";
|
|
9
9
|
import { SessionArchiveStore } from "./sessionArchiveStore.js";
|
|
10
10
|
import { findArchiveCandidateByIdOrPrefix, planSessionArchiveTree } from "./sessionArchiveTree.js";
|
|
11
11
|
import { deterministicSessionName, fallbackSessionName, generateShortSessionName } from "./sessionNameGenerator.js";
|
|
12
12
|
import { computeEditPreview } from "./editPreview.js";
|
|
13
|
-
import { createPiSessionManagerGateway } from "./piSessionManagerGateway.js";
|
|
14
13
|
import { attachmentsToInlineImages, saveAttachmentsToWorkspace } from "./attachmentService.js";
|
|
15
14
|
import { parsePromptAttachments } from "../../shared/promptAttachments.js";
|
|
16
|
-
import {
|
|
15
|
+
import { createPiSessionManagerGateway } from "./piSessionManagerGateway.js";
|
|
16
|
+
import { canonicalizeStoredCwd, cwdPathsEqual } from "../workingDirectory.js";
|
|
17
|
+
import { piWebDataDir } from "../../config.js";
|
|
18
|
+
import { createScopedSettingsManager, resolveSettingsScopeDirectory } from "./projectSettingsScope.js";
|
|
17
19
|
import { createSpawnSessionToolDefinition } from "./spawnSessionTool.js";
|
|
18
20
|
import { createSubsessionToolDefinitions } from "./spawnSubsessionTool.js";
|
|
19
21
|
import { buildTranscriptView } from "./subsessionTranscript.js";
|
|
@@ -81,46 +83,51 @@ function pathInsideOrEqual(parent, child) {
|
|
|
81
83
|
function defaultCreateAgentRuntime(createRuntime, options) {
|
|
82
84
|
if (!(options.sessionManager instanceof SessionManager))
|
|
83
85
|
throw new Error("Default runtime creation requires an SDK SessionManager");
|
|
84
|
-
const runtimeFactory = createRuntimeWithContextAndOneShotInitialModel(createRuntime, options.managementContext, options.initialModel);
|
|
86
|
+
const runtimeFactory = createRuntimeWithContextAndOneShotInitialModel(createRuntime, options.managementContext, options.initialModel, options.delegationToolsEnabled);
|
|
85
87
|
return createAgentSessionRuntime(runtimeFactory, {
|
|
86
88
|
cwd: options.cwd,
|
|
87
89
|
agentDir: options.agentDir,
|
|
88
90
|
sessionManager: options.sessionManager,
|
|
89
91
|
});
|
|
90
92
|
}
|
|
91
|
-
function createRuntimeWithContextAndOneShotInitialModel(createRuntime, managementContext, initialModel) {
|
|
93
|
+
function createRuntimeWithContextAndOneShotInitialModel(createRuntime, managementContext, initialModel, delegationToolsEnabled) {
|
|
92
94
|
// The inherited model belongs only to the session being spawned. Do not keep
|
|
93
95
|
// reapplying it if that runtime later creates/forks/switches sessions itself.
|
|
94
96
|
let pendingInitialModel = initialModel;
|
|
97
|
+
let pendingDelegationToolsEnabled = delegationToolsEnabled;
|
|
95
98
|
return async (options) => {
|
|
96
99
|
const model = pendingInitialModel;
|
|
100
|
+
const toolsEnabled = pendingDelegationToolsEnabled;
|
|
97
101
|
pendingInitialModel = undefined;
|
|
102
|
+
pendingDelegationToolsEnabled = undefined;
|
|
98
103
|
return createRuntime({
|
|
99
104
|
...options,
|
|
100
105
|
...(managementContext === undefined ? {} : { managementContext }),
|
|
101
106
|
...(model === undefined ? {} : { initialModel: model }),
|
|
107
|
+
...(toolsEnabled === undefined ? {} : { delegationToolsEnabled: toolsEnabled }),
|
|
102
108
|
});
|
|
103
109
|
};
|
|
104
110
|
}
|
|
105
|
-
function createDefaultRuntimeFactory(
|
|
106
|
-
return async ({ cwd, agentDir, sessionManager, sessionStartEvent, managementContext, initialModel }) => {
|
|
107
|
-
const
|
|
108
|
-
const authStorage = scopedModelRegistry.authStorage;
|
|
111
|
+
function createDefaultRuntimeFactory(normalModelRuntimeForCwd, managementModelRuntime, resolveSettingsScope, spawn, subsessions) {
|
|
112
|
+
return async ({ cwd, agentDir, sessionManager, sessionStartEvent, managementContext, initialModel, delegationToolsEnabled = true }) => {
|
|
113
|
+
const scopedModelRuntime = managementContext === undefined ? await normalModelRuntimeForCwd(cwd) : await managementModelRuntime();
|
|
109
114
|
if (managementContext !== undefined) {
|
|
110
|
-
return createManagementRuntimeFactory(
|
|
115
|
+
return createManagementRuntimeFactory(scopedModelRuntime, resolveSettingsScope, spawn, subsessions, managementContext)({
|
|
111
116
|
cwd,
|
|
112
117
|
agentDir,
|
|
113
118
|
sessionManager,
|
|
119
|
+
delegationToolsEnabled,
|
|
114
120
|
...(sessionStartEvent === undefined ? {} : { sessionStartEvent }),
|
|
115
121
|
...(initialModel === undefined ? {} : { initialModel }),
|
|
116
122
|
});
|
|
117
123
|
}
|
|
118
|
-
const
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
+
const settingsManager = await createScopedSettingsManager({
|
|
125
|
+
cwd,
|
|
126
|
+
scopeDirectory: await resolveSettingsScope(cwd, "normal"),
|
|
127
|
+
globalAgentDir: agentDir,
|
|
128
|
+
});
|
|
129
|
+
const services = await createAgentSessionServices({ cwd, agentDir, modelRuntime: scopedModelRuntime, settingsManager });
|
|
130
|
+
const customTools = createPiWebCustomToolDefinitions(cwd, delegationToolsEnabled, spawn, subsessions);
|
|
124
131
|
const result = await createAgentSessionFromServices({
|
|
125
132
|
services,
|
|
126
133
|
sessionManager,
|
|
@@ -131,15 +138,20 @@ function createDefaultRuntimeFactory(modelRegistry, managementModelRegistry, spa
|
|
|
131
138
|
return { ...result, services, diagnostics: services.diagnostics };
|
|
132
139
|
};
|
|
133
140
|
}
|
|
134
|
-
function createManagementRuntimeFactory(
|
|
135
|
-
return async ({ cwd, agentDir, sessionManager, sessionStartEvent, initialModel }) => {
|
|
141
|
+
function createManagementRuntimeFactory(modelRuntime, resolveSettingsScope, spawn, subsessions, managementContext) {
|
|
142
|
+
return async ({ cwd, agentDir, sessionManager, sessionStartEvent, initialModel, delegationToolsEnabled = true }) => {
|
|
136
143
|
const policyAgentDir = await writeManagementPermissionSystemPolicy(agentDir, cwd, managementContext);
|
|
137
144
|
return withRuntimeCreationEnvironment({ [PI_PERMISSION_SYSTEM_POLICY_AGENT_DIR]: policyAgentDir }, async () => {
|
|
145
|
+
const settingsManager = await createScopedSettingsManager({
|
|
146
|
+
cwd,
|
|
147
|
+
scopeDirectory: await resolveSettingsScope(cwd, "management"),
|
|
148
|
+
globalAgentDir: agentDir,
|
|
149
|
+
});
|
|
138
150
|
const services = await createAgentSessionServices({
|
|
139
151
|
cwd,
|
|
140
152
|
agentDir,
|
|
141
|
-
|
|
142
|
-
|
|
153
|
+
modelRuntime,
|
|
154
|
+
settingsManager,
|
|
143
155
|
resourceLoaderOptions: {
|
|
144
156
|
agentsFilesOverride: (base) => filterManagedGlobalContextFiles(cwd, agentDir, base),
|
|
145
157
|
},
|
|
@@ -148,8 +160,8 @@ function createManagementRuntimeFactory(authStorage, modelRegistry, spawn, subse
|
|
|
148
160
|
const customTools = [
|
|
149
161
|
...managedAgentToolDefinitions(cwd, managedToolOptions),
|
|
150
162
|
createManagedPythonToolDefinition(cwd, managementContext),
|
|
151
|
-
...(spawn
|
|
152
|
-
...(subsessions
|
|
163
|
+
...(delegationToolsEnabled && spawn !== undefined ? [defineTool(createSpawnSessionToolDefinition(cwd, { spawn }))] : []),
|
|
164
|
+
...(delegationToolsEnabled && subsessions !== undefined ? createSubsessionToolDefinitions(cwd, subsessions).map((tool) => defineTool(tool)) : []),
|
|
153
165
|
];
|
|
154
166
|
const options = {
|
|
155
167
|
services,
|
|
@@ -164,6 +176,13 @@ function createManagementRuntimeFactory(authStorage, modelRegistry, spawn, subse
|
|
|
164
176
|
});
|
|
165
177
|
};
|
|
166
178
|
}
|
|
179
|
+
export function createPiWebCustomToolDefinitions(cwd, delegationEnabled, spawn, subsessions) {
|
|
180
|
+
return [
|
|
181
|
+
...managedAgentToolDefinitions(cwd),
|
|
182
|
+
...(delegationEnabled && spawn !== undefined ? [defineTool(createSpawnSessionToolDefinition(cwd, { spawn }))] : []),
|
|
183
|
+
...(delegationEnabled && subsessions !== undefined ? createSubsessionToolDefinitions(cwd, subsessions).map((tool) => defineTool(tool)) : []),
|
|
184
|
+
];
|
|
185
|
+
}
|
|
167
186
|
function managedAgentToolDefinitions(cwd, options) {
|
|
168
187
|
if (options === undefined)
|
|
169
188
|
return [createPiWebEditToolDefinition(cwd)];
|
|
@@ -198,9 +217,10 @@ function createPiWebEditToolDefinition(cwd, options) {
|
|
|
198
217
|
});
|
|
199
218
|
}
|
|
200
219
|
export class PiSessionService {
|
|
201
|
-
constructor(events, deps
|
|
220
|
+
constructor(events, deps) {
|
|
202
221
|
this.events = events;
|
|
203
222
|
this.active = new Map();
|
|
223
|
+
this.pendingSessionOpens = new Map();
|
|
204
224
|
this.activities = new Map();
|
|
205
225
|
this.compactionPromptQueues = new Map();
|
|
206
226
|
this.compactionDrainTimers = new Map();
|
|
@@ -221,16 +241,30 @@ export class PiSessionService {
|
|
|
221
241
|
this.subsessionNotifyArmed = new Map();
|
|
222
242
|
this.archiveStore = deps.archiveStore ?? new SessionArchiveStore();
|
|
223
243
|
this.agentDir = deps.agentDir ?? getAgentDir();
|
|
224
|
-
this.
|
|
225
|
-
this.
|
|
226
|
-
this.
|
|
244
|
+
this.dataDir = deps.dataDir ?? piWebDataDir();
|
|
245
|
+
this.projectPathForCwd = deps.projectPathForCwd;
|
|
246
|
+
this.sessionManager = deps.sessionManager ?? createPiSessionManagerGateway({
|
|
247
|
+
agentDir: this.agentDir,
|
|
248
|
+
env: process.env,
|
|
249
|
+
sessionDirEnvKeys: ["PI_WEB_AGENT_SESSION_DIR", "PI_CODING_AGENT_SESSION_DIR"],
|
|
250
|
+
});
|
|
251
|
+
let defaultModelRuntime;
|
|
252
|
+
const resolveDefaultModelRuntime = () => {
|
|
253
|
+
defaultModelRuntime ??= deps.modelRuntime === undefined ? ModelRuntime.create() : Promise.resolve(deps.modelRuntime);
|
|
254
|
+
return defaultModelRuntime;
|
|
255
|
+
};
|
|
256
|
+
this.normalModelRuntimeForCwd = deps.normalModelRuntimeForCwd ?? resolveDefaultModelRuntime;
|
|
257
|
+
const managementModelRuntime = deps.managementModelRuntime;
|
|
258
|
+
this.managementModelRuntime = managementModelRuntime === undefined
|
|
259
|
+
? resolveDefaultModelRuntime
|
|
260
|
+
: () => Promise.resolve(managementModelRuntime);
|
|
227
261
|
this.spawnTargets = deps.spawnTargets;
|
|
228
262
|
this.logger = deps.logger ?? noopLogger;
|
|
229
263
|
this.now = deps.now ?? (() => new Date());
|
|
230
264
|
// Subsessions are a beta capability gated behind their own flag, and they
|
|
231
265
|
// also require the spawn capability (they share its project-scope resolver).
|
|
232
266
|
const subsessionsActive = this.spawnTargets !== undefined && deps.subsessionsEnabled === true;
|
|
233
|
-
this.createRuntime = deps.createRuntime ?? createDefaultRuntimeFactory(this.
|
|
267
|
+
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 : {
|
|
234
268
|
spawn: (input) => this.spawnSubsession(input),
|
|
235
269
|
list: (parentSessionId, parentSessionFile) => this.listSubsessions(parentSessionId, parentSessionFile),
|
|
236
270
|
check: (parentSessionId, sessionId, parentSessionFile) => this.checkSubsession(parentSessionId, sessionId, parentSessionFile),
|
|
@@ -254,6 +288,15 @@ export class PiSessionService {
|
|
|
254
288
|
activeCount() {
|
|
255
289
|
return this.active.size;
|
|
256
290
|
}
|
|
291
|
+
async resolveSettingsScopeDirectory(cwd, mode) {
|
|
292
|
+
const projectPath = this.projectPathForCwd === undefined ? undefined : await this.projectPathForCwd(cwd);
|
|
293
|
+
return resolveSettingsScopeDirectory({
|
|
294
|
+
dataDir: this.dataDir,
|
|
295
|
+
cwd,
|
|
296
|
+
mode,
|
|
297
|
+
...(projectPath === undefined ? {} : { projectPath }),
|
|
298
|
+
});
|
|
299
|
+
}
|
|
257
300
|
async cleanupPreview(request) {
|
|
258
301
|
return previewResponseFromPlan(await this.cleanupPlan(request));
|
|
259
302
|
}
|
|
@@ -298,8 +341,12 @@ export class PiSessionService {
|
|
|
298
341
|
async dispose() {
|
|
299
342
|
clearInterval(this.heartbeat);
|
|
300
343
|
this.clearCompactionDrainTimers();
|
|
344
|
+
const pendingOpens = this.pendingSessionOpenPromises();
|
|
345
|
+
if (pendingOpens.length > 0)
|
|
346
|
+
await Promise.allSettled(pendingOpens);
|
|
301
347
|
const activeSessions = Array.from(new Set(this.active.values()));
|
|
302
348
|
this.active.clear();
|
|
349
|
+
this.pendingSessionOpens.clear();
|
|
303
350
|
this.activities.clear();
|
|
304
351
|
this.compactionPromptQueues.clear();
|
|
305
352
|
this.authLossWarnings.clear();
|
|
@@ -316,6 +363,8 @@ export class PiSessionService {
|
|
|
316
363
|
}));
|
|
317
364
|
}
|
|
318
365
|
async list(cwd, managementContext) {
|
|
366
|
+
if (managementContext === undefined)
|
|
367
|
+
await this.normalModelRuntimeForCwd(cwd);
|
|
319
368
|
const [sessions, archivedRecords] = await Promise.all([this.sessionManager.list(cwd), this.archiveStore.list()]);
|
|
320
369
|
const sessionsById = new Map(sessions.map((session) => [session.id, session]));
|
|
321
370
|
const archivedForCwd = await Promise.all(archivedRecords
|
|
@@ -331,9 +380,15 @@ export class PiSessionService {
|
|
|
331
380
|
return [...unarchivedSessions, ...archivedSessions];
|
|
332
381
|
}
|
|
333
382
|
async start(cwd, options = {}) {
|
|
383
|
+
if (options.managementContext === undefined)
|
|
384
|
+
await this.normalModelRuntimeForCwd(cwd);
|
|
385
|
+
return this.startSession(cwd, options);
|
|
386
|
+
}
|
|
387
|
+
async startSession(cwd, options) {
|
|
334
388
|
const active = await this.create(this.sessionManager.create(cwd, options.parentSession === undefined ? undefined : { parentSession: options.parentSession }), cwd, {
|
|
335
389
|
...(options.managementContext === undefined ? {} : { managementContext: options.managementContext }),
|
|
336
390
|
...(options.initialModel === undefined ? {} : { initialModel: options.initialModel }),
|
|
391
|
+
...(options.creationProvenance === undefined ? {} : { creationProvenance: options.creationProvenance }),
|
|
337
392
|
});
|
|
338
393
|
const { session } = active.runtime;
|
|
339
394
|
const created = {
|
|
@@ -382,9 +437,10 @@ export class PiSessionService {
|
|
|
382
437
|
const decision = await this.spawnTargets.resolveSpawnTarget(input.spawningCwd, input.cwd);
|
|
383
438
|
if (!decision.allowed)
|
|
384
439
|
throw spawnTargetError(decision);
|
|
385
|
-
const created = await this.
|
|
440
|
+
const created = await this.startSession(decision.cwd, {
|
|
386
441
|
...(input.parentSessionFile === undefined ? {} : { parentSession: input.parentSessionFile }),
|
|
387
442
|
...(input.model === undefined ? {} : { initialModel: input.model }),
|
|
443
|
+
creationProvenance: "tracked-subsession",
|
|
388
444
|
});
|
|
389
445
|
const parentSessionFile = nonEmptyString(input.parentSessionFile);
|
|
390
446
|
const link = {
|
|
@@ -579,61 +635,13 @@ export class PiSessionService {
|
|
|
579
635
|
return;
|
|
580
636
|
this.registerVerifiedSubsession(link);
|
|
581
637
|
}
|
|
582
|
-
|
|
583
|
-
|
|
584
|
-
|
|
585
|
-
|
|
586
|
-
|
|
587
|
-
|
|
588
|
-
|
|
589
|
-
if (parsed?.spawnedSessionId === session.sessionId)
|
|
590
|
-
marker = parsed;
|
|
591
|
-
}
|
|
592
|
-
if (marker === undefined)
|
|
593
|
-
return undefined;
|
|
594
|
-
const childSessionFile = nonEmptyString(session.sessionFile);
|
|
595
|
-
if (childSessionFile === undefined)
|
|
596
|
-
return undefined;
|
|
597
|
-
const childHeader = await readSessionHeaderSummary(childSessionFile);
|
|
598
|
-
if (childHeader?.id !== session.sessionId)
|
|
599
|
-
return undefined;
|
|
600
|
-
const parentSessionFile = nonEmptyString(childHeader.parentSession);
|
|
601
|
-
if (parentSessionFile === undefined)
|
|
602
|
-
return undefined;
|
|
603
|
-
const parentHeader = await readSessionHeaderSummary(parentSessionFile);
|
|
604
|
-
if (parentHeader?.id !== marker.spawnedBySessionId)
|
|
605
|
-
return undefined;
|
|
606
|
-
const parentLink = this.findReciprocalParentSubsessionLink(parentSessionFile, marker.spawnedBySessionId, session.sessionId, childSessionFile);
|
|
607
|
-
if (parentLink === undefined)
|
|
608
|
-
return undefined;
|
|
609
|
-
return {
|
|
610
|
-
parentSessionId: marker.spawnedBySessionId,
|
|
611
|
-
childSessionId: session.sessionId,
|
|
612
|
-
childSessionFile,
|
|
613
|
-
parentSessionFile,
|
|
614
|
-
cwd: parentLink.cwd ?? session.sessionManager.getCwd(),
|
|
615
|
-
};
|
|
616
|
-
}
|
|
617
|
-
findReciprocalParentSubsessionLink(parentSessionFile, parentSessionId, childSessionId, childSessionFile) {
|
|
618
|
-
let parentManager;
|
|
619
|
-
try {
|
|
620
|
-
parentManager = this.sessionManager.open(parentSessionFile);
|
|
621
|
-
}
|
|
622
|
-
catch {
|
|
623
|
-
return undefined;
|
|
624
|
-
}
|
|
625
|
-
const entries = parentManager.getEntries?.() ?? parentManager.getBranch();
|
|
626
|
-
for (const entry of entries) {
|
|
627
|
-
const link = parsePersistedParentSubsessionLink(entry);
|
|
628
|
-
if (link === undefined)
|
|
629
|
-
continue;
|
|
630
|
-
if (link.spawnedBySessionId !== parentSessionId || link.spawnedSessionId !== childSessionId)
|
|
631
|
-
continue;
|
|
632
|
-
if (link.spawnedSessionFile === undefined || !sessionPathsEqual(link.spawnedSessionFile, childSessionFile))
|
|
633
|
-
continue;
|
|
634
|
-
return link;
|
|
635
|
-
}
|
|
636
|
-
return undefined;
|
|
638
|
+
verifiedSubsessionLinkFromOpenedChild(session) {
|
|
639
|
+
return verifiedTrackedSubsessionLink(this.sessionManager, {
|
|
640
|
+
sessionId: session.sessionId,
|
|
641
|
+
sessionFile: session.sessionFile,
|
|
642
|
+
sessionManager: session.sessionManager,
|
|
643
|
+
cwd: session.sessionManager.getCwd(),
|
|
644
|
+
});
|
|
637
645
|
}
|
|
638
646
|
async getOrOpenTrackedSubsession(sessionId) {
|
|
639
647
|
const link = this.subsessionLinks.get(sessionId);
|
|
@@ -670,6 +678,16 @@ export class PiSessionService {
|
|
|
670
678
|
return "error";
|
|
671
679
|
return "idle";
|
|
672
680
|
}
|
|
681
|
+
workingSubsessionIds(parentSessionId) {
|
|
682
|
+
const childIds = this.subsessionChildren.get(parentSessionId);
|
|
683
|
+
if (childIds === undefined)
|
|
684
|
+
return [];
|
|
685
|
+
return [...childIds].filter((childId) => {
|
|
686
|
+
const link = this.subsessionLinks.get(childId);
|
|
687
|
+
const active = link === undefined ? undefined : this.activeChildForSubsessionLink(link);
|
|
688
|
+
return active !== undefined && this.hasActiveWork(active.runtime.session);
|
|
689
|
+
});
|
|
690
|
+
}
|
|
673
691
|
/**
|
|
674
692
|
* Drive parent notifications from a tracked child's status. Arms a pending
|
|
675
693
|
* notification while the child is working, and when it stops fires a single
|
|
@@ -691,7 +709,11 @@ export class PiSessionService {
|
|
|
691
709
|
const status = this.activityForSession(session)?.phase === "error" ? "error" : "idle";
|
|
692
710
|
const finalText = finalAssistantText(historyMessages(session));
|
|
693
711
|
const preview = finalText === "" ? "(no output)" : truncateForNotification(finalText);
|
|
694
|
-
const
|
|
712
|
+
const workingIds = this.workingSubsessionIds(link.parentSessionId);
|
|
713
|
+
const next = workingIds.length === 0
|
|
714
|
+
? "No other tracked subsessions are working."
|
|
715
|
+
: `Still working: ${workingIds.join(", ")}. Continue working, or call yield_to_subsessions alone and last at the next join point. Further completion notices arrive automatically; do not poll.`;
|
|
716
|
+
const text = `Subsession ${childId} stopped working (${status}).\n${next}\n\n--- SUBSESSION OUTPUT: ${childId} ---\n${preview}`;
|
|
695
717
|
void this.notifyParentOfSubsession(link.parentSessionId, childId, text);
|
|
696
718
|
}
|
|
697
719
|
async getOrOpenParentForSubsession(parentSessionId, childSessionId) {
|
|
@@ -736,21 +758,21 @@ export class PiSessionService {
|
|
|
736
758
|
}
|
|
737
759
|
async availableModels(ref, managementContext) {
|
|
738
760
|
const session = await this.getOrOpen(ref, managementContext);
|
|
739
|
-
session.
|
|
761
|
+
await session.modelRuntime.reloadConfig();
|
|
740
762
|
const models = session.scopedModels.length > 0
|
|
741
763
|
? session.scopedModels.map((scoped) => scoped.model)
|
|
742
|
-
: session.
|
|
764
|
+
: await session.modelRuntime.getAvailable();
|
|
743
765
|
return models.map(modelToClientModel);
|
|
744
766
|
}
|
|
745
767
|
async setModel(ref, provider, modelId, managementContext) {
|
|
746
768
|
await this.assertWritable(ref);
|
|
747
769
|
const session = await this.getOrOpen(ref, managementContext);
|
|
748
|
-
session.
|
|
770
|
+
await session.modelRuntime.reloadConfig();
|
|
749
771
|
const candidates = session.scopedModels.length > 0
|
|
750
772
|
? session.scopedModels.map((scoped) => scoped.model)
|
|
751
|
-
: session.
|
|
773
|
+
: await session.modelRuntime.getAvailable();
|
|
752
774
|
const model = candidates.find((candidate) => candidate.provider === provider && candidate.id === modelId)
|
|
753
|
-
?? session.
|
|
775
|
+
?? session.modelRuntime.getModel(provider, modelId);
|
|
754
776
|
if (model === undefined)
|
|
755
777
|
throw new Error(`未找到模型:${provider}/${modelId}`);
|
|
756
778
|
await session.setModel(model);
|
|
@@ -1121,6 +1143,14 @@ export class PiSessionService {
|
|
|
1121
1143
|
this.publishActivity(active.runtime.session, "stopped", "idle");
|
|
1122
1144
|
this.publishStatus(active.runtime.session);
|
|
1123
1145
|
}
|
|
1146
|
+
async clearQueue(ref, managementContext) {
|
|
1147
|
+
await this.assertWritable(ref);
|
|
1148
|
+
const session = await this.getOrOpen(ref, managementContext);
|
|
1149
|
+
this.clearCompactionPromptQueue(session.sessionId);
|
|
1150
|
+
clearSessionQueue(session);
|
|
1151
|
+
this.publishStatus(session);
|
|
1152
|
+
return this.statusFromSession(session);
|
|
1153
|
+
}
|
|
1124
1154
|
stop(ref, managementContext) {
|
|
1125
1155
|
const active = this.activeForLookup(ref, managementContext);
|
|
1126
1156
|
if (active === undefined)
|
|
@@ -1316,6 +1346,9 @@ export class PiSessionService {
|
|
|
1316
1346
|
return [...names];
|
|
1317
1347
|
}
|
|
1318
1348
|
async closeActive(sessionId) {
|
|
1349
|
+
const pendingOpens = this.pendingSessionOpenPromises(sessionId);
|
|
1350
|
+
if (pendingOpens.length > 0)
|
|
1351
|
+
await Promise.allSettled(pendingOpens);
|
|
1319
1352
|
const activeSessions = this.activeSessionsForId(sessionId);
|
|
1320
1353
|
await Promise.all(activeSessions.map((active) => this.closeActiveSession(active)));
|
|
1321
1354
|
}
|
|
@@ -1358,14 +1391,36 @@ export class PiSessionService {
|
|
|
1358
1391
|
return active;
|
|
1359
1392
|
const archived = await this.getArchived(ref);
|
|
1360
1393
|
if (archived?.archivePath !== undefined) {
|
|
1361
|
-
|
|
1394
|
+
const archivePath = archived.archivePath;
|
|
1395
|
+
return this.openPending(archived.sessionId, archived.cwd, managementContext, () => this.sessionManager.open(archivePath));
|
|
1362
1396
|
}
|
|
1363
1397
|
const match = isPiSessionRef(ref)
|
|
1364
1398
|
? (await this.sessionManager.list(ref.cwd)).find((s) => s.id === ref.id || s.id.startsWith(ref.id))
|
|
1365
1399
|
: (await this.sessionManager.listAll?.() ?? []).find((s) => s.id === ref || s.id.startsWith(ref));
|
|
1366
1400
|
if (!match)
|
|
1367
1401
|
throw new Error("未找到会话");
|
|
1368
|
-
return this.
|
|
1402
|
+
return this.openPending(match.id, match.cwd, managementContext, () => this.sessionManager.open(match.path));
|
|
1403
|
+
}
|
|
1404
|
+
openPending(sessionId, cwd, managementContext, openSessionManager) {
|
|
1405
|
+
const key = JSON.stringify([canonicalizeStoredCwd(cwd), sessionId, managementContextKey(managementContext)]);
|
|
1406
|
+
const existing = this.pendingSessionOpens.get(key);
|
|
1407
|
+
if (existing !== undefined)
|
|
1408
|
+
return existing.promise;
|
|
1409
|
+
const pending = {
|
|
1410
|
+
sessionId,
|
|
1411
|
+
promise: this.create(openSessionManager(), cwd, managementContext === undefined ? {} : { managementContext }),
|
|
1412
|
+
};
|
|
1413
|
+
pending.promise = pending.promise.finally(() => {
|
|
1414
|
+
if (this.pendingSessionOpens.get(key) === pending)
|
|
1415
|
+
this.pendingSessionOpens.delete(key);
|
|
1416
|
+
});
|
|
1417
|
+
this.pendingSessionOpens.set(key, pending);
|
|
1418
|
+
return pending.promise;
|
|
1419
|
+
}
|
|
1420
|
+
pendingSessionOpenPromises(sessionId) {
|
|
1421
|
+
return [...this.pendingSessionOpens.values()]
|
|
1422
|
+
.filter((pending) => sessionId === undefined || pending.sessionId === sessionId)
|
|
1423
|
+
.map((pending) => pending.promise);
|
|
1369
1424
|
}
|
|
1370
1425
|
async getArchived(ref) {
|
|
1371
1426
|
const archived = await this.archiveStore.get(sessionIdFromLookup(ref));
|
|
@@ -1425,25 +1480,53 @@ export class PiSessionService {
|
|
|
1425
1480
|
return [...new Set(this.active.values())].filter((active) => active.runtime.session.sessionId === sessionId);
|
|
1426
1481
|
}
|
|
1427
1482
|
async create(sessionManager, cwd, options = {}) {
|
|
1483
|
+
const delegationToolsEnabled = options.creationProvenance !== "tracked-subsession"
|
|
1484
|
+
&& await sessionAllowsDelegationTools(sessionManager, this.sessionManager);
|
|
1428
1485
|
const runtime = await this.createAgentRuntime(this.createRuntime, {
|
|
1429
1486
|
cwd,
|
|
1430
1487
|
agentDir: this.agentDir,
|
|
1431
1488
|
sessionManager,
|
|
1489
|
+
delegationToolsEnabled,
|
|
1432
1490
|
...(options.managementContext === undefined ? {} : { managementContext: options.managementContext }),
|
|
1433
1491
|
...(options.initialModel === undefined ? {} : { initialModel: options.initialModel }),
|
|
1434
1492
|
});
|
|
1435
1493
|
const active = { runtime, unsubscribe: noop, managementContextKey: managementContextKey(options.managementContext), eventScope: eventScopeFromManagementContext(options.managementContext) };
|
|
1436
|
-
|
|
1437
|
-
|
|
1438
|
-
this.bindRuntime(active);
|
|
1439
|
-
runtime.setRebindSession(async (session) => {
|
|
1440
|
-
await this.bindSessionExtensions(session);
|
|
1494
|
+
try {
|
|
1495
|
+
await this.bindSessionExtensions(runtime.session);
|
|
1441
1496
|
this.bindRuntime(active);
|
|
1442
|
-
|
|
1443
|
-
|
|
1444
|
-
|
|
1445
|
-
|
|
1446
|
-
|
|
1497
|
+
runtime.setRebindSession(async (session) => {
|
|
1498
|
+
await this.bindSessionExtensions(session);
|
|
1499
|
+
this.bindRuntime(active);
|
|
1500
|
+
await this.recoverSubsessionTrackingForOpenedSession(session);
|
|
1501
|
+
});
|
|
1502
|
+
this.active.set(activeSessionKey(runtime.session.sessionId, active.eventScope), active);
|
|
1503
|
+
await this.recoverSubsessionTrackingForOpenedSession(runtime.session);
|
|
1504
|
+
this.publishStatus(runtime.session);
|
|
1505
|
+
return active;
|
|
1506
|
+
}
|
|
1507
|
+
catch (error) {
|
|
1508
|
+
active.unsubscribe();
|
|
1509
|
+
let removedActive = false;
|
|
1510
|
+
for (const [sessionKey, candidate] of this.active.entries()) {
|
|
1511
|
+
if (candidate !== active)
|
|
1512
|
+
continue;
|
|
1513
|
+
this.active.delete(sessionKey);
|
|
1514
|
+
this.activities.delete(sessionKey);
|
|
1515
|
+
removedActive = true;
|
|
1516
|
+
}
|
|
1517
|
+
this.clearAuthLossWarningsForSession(runtime.session.sessionId);
|
|
1518
|
+
this.clearCompactionPromptQueue(runtime.session.sessionId);
|
|
1519
|
+
if (removedActive) {
|
|
1520
|
+
this.workspaceActivity?.removeSession(runtime.session.sessionId, runtime.session.sessionManager.getCwd(), active.eventScope);
|
|
1521
|
+
}
|
|
1522
|
+
try {
|
|
1523
|
+
await runtime.session.abort();
|
|
1524
|
+
}
|
|
1525
|
+
finally {
|
|
1526
|
+
await runtime.dispose();
|
|
1527
|
+
}
|
|
1528
|
+
throw error;
|
|
1529
|
+
}
|
|
1447
1530
|
}
|
|
1448
1531
|
async bindSessionExtensions(session) {
|
|
1449
1532
|
await session.bindExtensions({
|
|
@@ -1559,15 +1642,12 @@ export class PiSessionService {
|
|
|
1559
1642
|
session.setSessionName(name);
|
|
1560
1643
|
this.publishSessionName(session);
|
|
1561
1644
|
}
|
|
1562
|
-
applyAuthChange(change
|
|
1563
|
-
|
|
1564
|
-
this.managementModelRegistry.refresh();
|
|
1565
|
-
const changedScope = change.scope ?? "normal";
|
|
1645
|
+
applyAuthChange(change) {
|
|
1646
|
+
const changedRuntime = change.modelRuntime;
|
|
1566
1647
|
for (const active of this.active.values()) {
|
|
1567
|
-
if (authScopeFromEventScope(active.eventScope) !== changedScope)
|
|
1568
|
-
continue;
|
|
1569
1648
|
const { session } = active.runtime;
|
|
1570
|
-
session.
|
|
1649
|
+
if (session.modelRuntime !== changedRuntime)
|
|
1650
|
+
continue;
|
|
1571
1651
|
this.syncCurrentModelAuthWarning(session, active.eventScope, change.removedProviderId);
|
|
1572
1652
|
this.publishStatus(session);
|
|
1573
1653
|
}
|
|
@@ -1579,10 +1659,10 @@ export class PiSessionService {
|
|
|
1579
1659
|
if (model.provider === "unknown" && model.id === "unknown")
|
|
1580
1660
|
return;
|
|
1581
1661
|
const warningKey = authLossWarningKey(session.sessionId, eventScope, model.provider, model.id);
|
|
1582
|
-
const registered = session.
|
|
1662
|
+
const registered = session.modelRuntime.getModel(model.provider, model.id);
|
|
1583
1663
|
if (registered === undefined)
|
|
1584
1664
|
return;
|
|
1585
|
-
if (session.
|
|
1665
|
+
if (session.modelRuntime.hasConfiguredAuth(registered.provider)) {
|
|
1586
1666
|
this.authLossWarnings.delete(warningKey);
|
|
1587
1667
|
return;
|
|
1588
1668
|
}
|
|
@@ -1847,6 +1927,76 @@ function addSessionName(names, name) {
|
|
|
1847
1927
|
function isDefined(value) {
|
|
1848
1928
|
return value !== undefined;
|
|
1849
1929
|
}
|
|
1930
|
+
/**
|
|
1931
|
+
* Resolve the delegation capability from server-owned, persisted session
|
|
1932
|
+
* provenance. A copied marker is not enough: the child header and reciprocal
|
|
1933
|
+
* parent link must identify the exact same session files.
|
|
1934
|
+
*/
|
|
1935
|
+
export async function sessionAllowsDelegationTools(sessionManager, managers) {
|
|
1936
|
+
const trackedLink = await verifiedTrackedSubsessionLink(managers, {
|
|
1937
|
+
sessionId: sessionManager.getSessionId(),
|
|
1938
|
+
sessionFile: sessionManager.getSessionFile(),
|
|
1939
|
+
sessionManager,
|
|
1940
|
+
cwd: sessionManager.getCwd(),
|
|
1941
|
+
});
|
|
1942
|
+
return trackedLink === undefined;
|
|
1943
|
+
}
|
|
1944
|
+
async function verifiedTrackedSubsessionLink(managers, session) {
|
|
1945
|
+
// Child markers are only hints; the current child header and reciprocal
|
|
1946
|
+
// parent custom link must agree on the exact ids and files before relinking.
|
|
1947
|
+
const entries = session.sessionManager.getEntries?.() ?? session.sessionManager.getBranch();
|
|
1948
|
+
let marker;
|
|
1949
|
+
for (const entry of entries) {
|
|
1950
|
+
const parsed = parsePersistedChildSubsessionLink(entry);
|
|
1951
|
+
if (parsed?.spawnedSessionId === session.sessionId)
|
|
1952
|
+
marker = parsed;
|
|
1953
|
+
}
|
|
1954
|
+
if (marker === undefined)
|
|
1955
|
+
return undefined;
|
|
1956
|
+
const childSessionFile = nonEmptyString(session.sessionFile);
|
|
1957
|
+
if (childSessionFile === undefined)
|
|
1958
|
+
return undefined;
|
|
1959
|
+
const childHeader = await readSessionHeaderSummary(childSessionFile);
|
|
1960
|
+
if (childHeader?.id !== session.sessionId)
|
|
1961
|
+
return undefined;
|
|
1962
|
+
const parentSessionFile = nonEmptyString(childHeader.parentSession);
|
|
1963
|
+
if (parentSessionFile === undefined)
|
|
1964
|
+
return undefined;
|
|
1965
|
+
const parentHeader = await readSessionHeaderSummary(parentSessionFile);
|
|
1966
|
+
if (parentHeader?.id !== marker.spawnedBySessionId)
|
|
1967
|
+
return undefined;
|
|
1968
|
+
const parentLink = findReciprocalParentSubsessionLink(managers, parentSessionFile, marker.spawnedBySessionId, session.sessionId, childSessionFile);
|
|
1969
|
+
if (parentLink === undefined)
|
|
1970
|
+
return undefined;
|
|
1971
|
+
return {
|
|
1972
|
+
parentSessionId: marker.spawnedBySessionId,
|
|
1973
|
+
childSessionId: session.sessionId,
|
|
1974
|
+
childSessionFile,
|
|
1975
|
+
parentSessionFile,
|
|
1976
|
+
cwd: parentLink.cwd ?? session.cwd,
|
|
1977
|
+
};
|
|
1978
|
+
}
|
|
1979
|
+
function findReciprocalParentSubsessionLink(managers, parentSessionFile, parentSessionId, childSessionId, childSessionFile) {
|
|
1980
|
+
let parentManager;
|
|
1981
|
+
try {
|
|
1982
|
+
parentManager = managers.open(parentSessionFile);
|
|
1983
|
+
}
|
|
1984
|
+
catch {
|
|
1985
|
+
return undefined;
|
|
1986
|
+
}
|
|
1987
|
+
const entries = parentManager.getEntries?.() ?? parentManager.getBranch();
|
|
1988
|
+
for (const entry of entries) {
|
|
1989
|
+
const link = parsePersistedParentSubsessionLink(entry);
|
|
1990
|
+
if (link === undefined)
|
|
1991
|
+
continue;
|
|
1992
|
+
if (link.spawnedBySessionId !== parentSessionId || link.spawnedSessionId !== childSessionId)
|
|
1993
|
+
continue;
|
|
1994
|
+
if (link.spawnedSessionFile === undefined || !sessionPathsEqual(link.spawnedSessionFile, childSessionFile))
|
|
1995
|
+
continue;
|
|
1996
|
+
return link;
|
|
1997
|
+
}
|
|
1998
|
+
return undefined;
|
|
1999
|
+
}
|
|
1850
2000
|
function trackedSubsessionLinkFromParentLink(parentSessionId, link, parentSessionFile) {
|
|
1851
2001
|
return {
|
|
1852
2002
|
parentSessionId,
|