@chainingintention/pi-web-cn 1.202607.5-dev.0 → 1.202607.6
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 +102 -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 +237 -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/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-_Lzl-tA7.js +0 -4
- package/dist/client/assets/TerminalPanel-BVBJC-6w.js +0 -126
- package/dist/client/assets/UnifiedDiffViewer-BwONrl2w.js +0 -34
- package/dist/client/assets/index-Ce8_7l5M.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,19 @@
|
|
|
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
17
|
import { createSpawnSessionToolDefinition } from "./spawnSessionTool.js";
|
|
18
18
|
import { createSubsessionToolDefinitions } from "./spawnSubsessionTool.js";
|
|
19
19
|
import { buildTranscriptView } from "./subsessionTranscript.js";
|
|
@@ -81,46 +81,46 @@ function pathInsideOrEqual(parent, child) {
|
|
|
81
81
|
function defaultCreateAgentRuntime(createRuntime, options) {
|
|
82
82
|
if (!(options.sessionManager instanceof SessionManager))
|
|
83
83
|
throw new Error("Default runtime creation requires an SDK SessionManager");
|
|
84
|
-
const runtimeFactory = createRuntimeWithContextAndOneShotInitialModel(createRuntime, options.managementContext, options.initialModel);
|
|
84
|
+
const runtimeFactory = createRuntimeWithContextAndOneShotInitialModel(createRuntime, options.managementContext, options.initialModel, options.delegationToolsEnabled);
|
|
85
85
|
return createAgentSessionRuntime(runtimeFactory, {
|
|
86
86
|
cwd: options.cwd,
|
|
87
87
|
agentDir: options.agentDir,
|
|
88
88
|
sessionManager: options.sessionManager,
|
|
89
89
|
});
|
|
90
90
|
}
|
|
91
|
-
function createRuntimeWithContextAndOneShotInitialModel(createRuntime, managementContext, initialModel) {
|
|
91
|
+
function createRuntimeWithContextAndOneShotInitialModel(createRuntime, managementContext, initialModel, delegationToolsEnabled) {
|
|
92
92
|
// The inherited model belongs only to the session being spawned. Do not keep
|
|
93
93
|
// reapplying it if that runtime later creates/forks/switches sessions itself.
|
|
94
94
|
let pendingInitialModel = initialModel;
|
|
95
|
+
let pendingDelegationToolsEnabled = delegationToolsEnabled;
|
|
95
96
|
return async (options) => {
|
|
96
97
|
const model = pendingInitialModel;
|
|
98
|
+
const toolsEnabled = pendingDelegationToolsEnabled;
|
|
97
99
|
pendingInitialModel = undefined;
|
|
100
|
+
pendingDelegationToolsEnabled = undefined;
|
|
98
101
|
return createRuntime({
|
|
99
102
|
...options,
|
|
100
103
|
...(managementContext === undefined ? {} : { managementContext }),
|
|
101
104
|
...(model === undefined ? {} : { initialModel: model }),
|
|
105
|
+
...(toolsEnabled === undefined ? {} : { delegationToolsEnabled: toolsEnabled }),
|
|
102
106
|
});
|
|
103
107
|
};
|
|
104
108
|
}
|
|
105
|
-
function createDefaultRuntimeFactory(
|
|
106
|
-
return async ({ cwd, agentDir, sessionManager, sessionStartEvent, managementContext, initialModel }) => {
|
|
107
|
-
const
|
|
108
|
-
const authStorage = scopedModelRegistry.authStorage;
|
|
109
|
+
function createDefaultRuntimeFactory(normalModelRuntimeForCwd, managementModelRuntime, spawn, subsessions) {
|
|
110
|
+
return async ({ cwd, agentDir, sessionManager, sessionStartEvent, managementContext, initialModel, delegationToolsEnabled = true }) => {
|
|
111
|
+
const scopedModelRuntime = managementContext === undefined ? await normalModelRuntimeForCwd(cwd) : await managementModelRuntime();
|
|
109
112
|
if (managementContext !== undefined) {
|
|
110
|
-
return createManagementRuntimeFactory(
|
|
113
|
+
return createManagementRuntimeFactory(scopedModelRuntime, spawn, subsessions, managementContext)({
|
|
111
114
|
cwd,
|
|
112
115
|
agentDir,
|
|
113
116
|
sessionManager,
|
|
117
|
+
delegationToolsEnabled,
|
|
114
118
|
...(sessionStartEvent === undefined ? {} : { sessionStartEvent }),
|
|
115
119
|
...(initialModel === undefined ? {} : { initialModel }),
|
|
116
120
|
});
|
|
117
121
|
}
|
|
118
|
-
const services = await createAgentSessionServices({ cwd, agentDir,
|
|
119
|
-
const customTools =
|
|
120
|
-
...managedAgentToolDefinitions(cwd),
|
|
121
|
-
...(spawn === undefined ? [] : [defineTool(createSpawnSessionToolDefinition(cwd, { spawn }))]),
|
|
122
|
-
...(subsessions === undefined ? [] : createSubsessionToolDefinitions(cwd, subsessions).map((tool) => defineTool(tool))),
|
|
123
|
-
];
|
|
122
|
+
const services = await createAgentSessionServices({ cwd, agentDir, modelRuntime: scopedModelRuntime });
|
|
123
|
+
const customTools = createPiWebCustomToolDefinitions(cwd, delegationToolsEnabled, spawn, subsessions);
|
|
124
124
|
const result = await createAgentSessionFromServices({
|
|
125
125
|
services,
|
|
126
126
|
sessionManager,
|
|
@@ -131,15 +131,14 @@ function createDefaultRuntimeFactory(modelRegistry, managementModelRegistry, spa
|
|
|
131
131
|
return { ...result, services, diagnostics: services.diagnostics };
|
|
132
132
|
};
|
|
133
133
|
}
|
|
134
|
-
function createManagementRuntimeFactory(
|
|
135
|
-
return async ({ cwd, agentDir, sessionManager, sessionStartEvent, initialModel }) => {
|
|
134
|
+
function createManagementRuntimeFactory(modelRuntime, spawn, subsessions, managementContext) {
|
|
135
|
+
return async ({ cwd, agentDir, sessionManager, sessionStartEvent, initialModel, delegationToolsEnabled = true }) => {
|
|
136
136
|
const policyAgentDir = await writeManagementPermissionSystemPolicy(agentDir, cwd, managementContext);
|
|
137
137
|
return withRuntimeCreationEnvironment({ [PI_PERMISSION_SYSTEM_POLICY_AGENT_DIR]: policyAgentDir }, async () => {
|
|
138
138
|
const services = await createAgentSessionServices({
|
|
139
139
|
cwd,
|
|
140
140
|
agentDir,
|
|
141
|
-
|
|
142
|
-
modelRegistry,
|
|
141
|
+
modelRuntime,
|
|
143
142
|
resourceLoaderOptions: {
|
|
144
143
|
agentsFilesOverride: (base) => filterManagedGlobalContextFiles(cwd, agentDir, base),
|
|
145
144
|
},
|
|
@@ -148,8 +147,8 @@ function createManagementRuntimeFactory(authStorage, modelRegistry, spawn, subse
|
|
|
148
147
|
const customTools = [
|
|
149
148
|
...managedAgentToolDefinitions(cwd, managedToolOptions),
|
|
150
149
|
createManagedPythonToolDefinition(cwd, managementContext),
|
|
151
|
-
...(spawn
|
|
152
|
-
...(subsessions
|
|
150
|
+
...(delegationToolsEnabled && spawn !== undefined ? [defineTool(createSpawnSessionToolDefinition(cwd, { spawn }))] : []),
|
|
151
|
+
...(delegationToolsEnabled && subsessions !== undefined ? createSubsessionToolDefinitions(cwd, subsessions).map((tool) => defineTool(tool)) : []),
|
|
153
152
|
];
|
|
154
153
|
const options = {
|
|
155
154
|
services,
|
|
@@ -164,6 +163,13 @@ function createManagementRuntimeFactory(authStorage, modelRegistry, spawn, subse
|
|
|
164
163
|
});
|
|
165
164
|
};
|
|
166
165
|
}
|
|
166
|
+
export function createPiWebCustomToolDefinitions(cwd, delegationEnabled, spawn, subsessions) {
|
|
167
|
+
return [
|
|
168
|
+
...managedAgentToolDefinitions(cwd),
|
|
169
|
+
...(delegationEnabled && spawn !== undefined ? [defineTool(createSpawnSessionToolDefinition(cwd, { spawn }))] : []),
|
|
170
|
+
...(delegationEnabled && subsessions !== undefined ? createSubsessionToolDefinitions(cwd, subsessions).map((tool) => defineTool(tool)) : []),
|
|
171
|
+
];
|
|
172
|
+
}
|
|
167
173
|
function managedAgentToolDefinitions(cwd, options) {
|
|
168
174
|
if (options === undefined)
|
|
169
175
|
return [createPiWebEditToolDefinition(cwd)];
|
|
@@ -198,9 +204,10 @@ function createPiWebEditToolDefinition(cwd, options) {
|
|
|
198
204
|
});
|
|
199
205
|
}
|
|
200
206
|
export class PiSessionService {
|
|
201
|
-
constructor(events, deps
|
|
207
|
+
constructor(events, deps) {
|
|
202
208
|
this.events = events;
|
|
203
209
|
this.active = new Map();
|
|
210
|
+
this.pendingSessionOpens = new Map();
|
|
204
211
|
this.activities = new Map();
|
|
205
212
|
this.compactionPromptQueues = new Map();
|
|
206
213
|
this.compactionDrainTimers = new Map();
|
|
@@ -221,16 +228,28 @@ export class PiSessionService {
|
|
|
221
228
|
this.subsessionNotifyArmed = new Map();
|
|
222
229
|
this.archiveStore = deps.archiveStore ?? new SessionArchiveStore();
|
|
223
230
|
this.agentDir = deps.agentDir ?? getAgentDir();
|
|
224
|
-
this.sessionManager = deps.sessionManager ?? createPiSessionManagerGateway({
|
|
225
|
-
|
|
226
|
-
|
|
231
|
+
this.sessionManager = deps.sessionManager ?? createPiSessionManagerGateway({
|
|
232
|
+
agentDir: this.agentDir,
|
|
233
|
+
env: process.env,
|
|
234
|
+
sessionDirEnvKeys: ["PI_WEB_AGENT_SESSION_DIR", "PI_CODING_AGENT_SESSION_DIR"],
|
|
235
|
+
});
|
|
236
|
+
let defaultModelRuntime;
|
|
237
|
+
const resolveDefaultModelRuntime = () => {
|
|
238
|
+
defaultModelRuntime ??= deps.modelRuntime === undefined ? ModelRuntime.create() : Promise.resolve(deps.modelRuntime);
|
|
239
|
+
return defaultModelRuntime;
|
|
240
|
+
};
|
|
241
|
+
this.normalModelRuntimeForCwd = deps.normalModelRuntimeForCwd ?? resolveDefaultModelRuntime;
|
|
242
|
+
const managementModelRuntime = deps.managementModelRuntime;
|
|
243
|
+
this.managementModelRuntime = managementModelRuntime === undefined
|
|
244
|
+
? resolveDefaultModelRuntime
|
|
245
|
+
: () => Promise.resolve(managementModelRuntime);
|
|
227
246
|
this.spawnTargets = deps.spawnTargets;
|
|
228
247
|
this.logger = deps.logger ?? noopLogger;
|
|
229
248
|
this.now = deps.now ?? (() => new Date());
|
|
230
249
|
// Subsessions are a beta capability gated behind their own flag, and they
|
|
231
250
|
// also require the spawn capability (they share its project-scope resolver).
|
|
232
251
|
const subsessionsActive = this.spawnTargets !== undefined && deps.subsessionsEnabled === true;
|
|
233
|
-
this.createRuntime = deps.createRuntime ?? createDefaultRuntimeFactory(this.
|
|
252
|
+
this.createRuntime = deps.createRuntime ?? createDefaultRuntimeFactory(this.normalModelRuntimeForCwd, this.managementModelRuntime, this.spawnTargets === undefined ? undefined : (input) => this.spawnSession(input), !subsessionsActive ? undefined : {
|
|
234
253
|
spawn: (input) => this.spawnSubsession(input),
|
|
235
254
|
list: (parentSessionId, parentSessionFile) => this.listSubsessions(parentSessionId, parentSessionFile),
|
|
236
255
|
check: (parentSessionId, sessionId, parentSessionFile) => this.checkSubsession(parentSessionId, sessionId, parentSessionFile),
|
|
@@ -298,8 +317,12 @@ export class PiSessionService {
|
|
|
298
317
|
async dispose() {
|
|
299
318
|
clearInterval(this.heartbeat);
|
|
300
319
|
this.clearCompactionDrainTimers();
|
|
320
|
+
const pendingOpens = this.pendingSessionOpenPromises();
|
|
321
|
+
if (pendingOpens.length > 0)
|
|
322
|
+
await Promise.allSettled(pendingOpens);
|
|
301
323
|
const activeSessions = Array.from(new Set(this.active.values()));
|
|
302
324
|
this.active.clear();
|
|
325
|
+
this.pendingSessionOpens.clear();
|
|
303
326
|
this.activities.clear();
|
|
304
327
|
this.compactionPromptQueues.clear();
|
|
305
328
|
this.authLossWarnings.clear();
|
|
@@ -316,6 +339,8 @@ export class PiSessionService {
|
|
|
316
339
|
}));
|
|
317
340
|
}
|
|
318
341
|
async list(cwd, managementContext) {
|
|
342
|
+
if (managementContext === undefined)
|
|
343
|
+
await this.normalModelRuntimeForCwd(cwd);
|
|
319
344
|
const [sessions, archivedRecords] = await Promise.all([this.sessionManager.list(cwd), this.archiveStore.list()]);
|
|
320
345
|
const sessionsById = new Map(sessions.map((session) => [session.id, session]));
|
|
321
346
|
const archivedForCwd = await Promise.all(archivedRecords
|
|
@@ -331,9 +356,15 @@ export class PiSessionService {
|
|
|
331
356
|
return [...unarchivedSessions, ...archivedSessions];
|
|
332
357
|
}
|
|
333
358
|
async start(cwd, options = {}) {
|
|
359
|
+
if (options.managementContext === undefined)
|
|
360
|
+
await this.normalModelRuntimeForCwd(cwd);
|
|
361
|
+
return this.startSession(cwd, options);
|
|
362
|
+
}
|
|
363
|
+
async startSession(cwd, options) {
|
|
334
364
|
const active = await this.create(this.sessionManager.create(cwd, options.parentSession === undefined ? undefined : { parentSession: options.parentSession }), cwd, {
|
|
335
365
|
...(options.managementContext === undefined ? {} : { managementContext: options.managementContext }),
|
|
336
366
|
...(options.initialModel === undefined ? {} : { initialModel: options.initialModel }),
|
|
367
|
+
...(options.creationProvenance === undefined ? {} : { creationProvenance: options.creationProvenance }),
|
|
337
368
|
});
|
|
338
369
|
const { session } = active.runtime;
|
|
339
370
|
const created = {
|
|
@@ -382,9 +413,10 @@ export class PiSessionService {
|
|
|
382
413
|
const decision = await this.spawnTargets.resolveSpawnTarget(input.spawningCwd, input.cwd);
|
|
383
414
|
if (!decision.allowed)
|
|
384
415
|
throw spawnTargetError(decision);
|
|
385
|
-
const created = await this.
|
|
416
|
+
const created = await this.startSession(decision.cwd, {
|
|
386
417
|
...(input.parentSessionFile === undefined ? {} : { parentSession: input.parentSessionFile }),
|
|
387
418
|
...(input.model === undefined ? {} : { initialModel: input.model }),
|
|
419
|
+
creationProvenance: "tracked-subsession",
|
|
388
420
|
});
|
|
389
421
|
const parentSessionFile = nonEmptyString(input.parentSessionFile);
|
|
390
422
|
const link = {
|
|
@@ -579,61 +611,13 @@ export class PiSessionService {
|
|
|
579
611
|
return;
|
|
580
612
|
this.registerVerifiedSubsession(link);
|
|
581
613
|
}
|
|
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;
|
|
614
|
+
verifiedSubsessionLinkFromOpenedChild(session) {
|
|
615
|
+
return verifiedTrackedSubsessionLink(this.sessionManager, {
|
|
616
|
+
sessionId: session.sessionId,
|
|
617
|
+
sessionFile: session.sessionFile,
|
|
618
|
+
sessionManager: session.sessionManager,
|
|
619
|
+
cwd: session.sessionManager.getCwd(),
|
|
620
|
+
});
|
|
637
621
|
}
|
|
638
622
|
async getOrOpenTrackedSubsession(sessionId) {
|
|
639
623
|
const link = this.subsessionLinks.get(sessionId);
|
|
@@ -670,6 +654,16 @@ export class PiSessionService {
|
|
|
670
654
|
return "error";
|
|
671
655
|
return "idle";
|
|
672
656
|
}
|
|
657
|
+
workingSubsessionIds(parentSessionId) {
|
|
658
|
+
const childIds = this.subsessionChildren.get(parentSessionId);
|
|
659
|
+
if (childIds === undefined)
|
|
660
|
+
return [];
|
|
661
|
+
return [...childIds].filter((childId) => {
|
|
662
|
+
const link = this.subsessionLinks.get(childId);
|
|
663
|
+
const active = link === undefined ? undefined : this.activeChildForSubsessionLink(link);
|
|
664
|
+
return active !== undefined && this.hasActiveWork(active.runtime.session);
|
|
665
|
+
});
|
|
666
|
+
}
|
|
673
667
|
/**
|
|
674
668
|
* Drive parent notifications from a tracked child's status. Arms a pending
|
|
675
669
|
* notification while the child is working, and when it stops fires a single
|
|
@@ -691,7 +685,11 @@ export class PiSessionService {
|
|
|
691
685
|
const status = this.activityForSession(session)?.phase === "error" ? "error" : "idle";
|
|
692
686
|
const finalText = finalAssistantText(historyMessages(session));
|
|
693
687
|
const preview = finalText === "" ? "(no output)" : truncateForNotification(finalText);
|
|
694
|
-
const
|
|
688
|
+
const workingIds = this.workingSubsessionIds(link.parentSessionId);
|
|
689
|
+
const next = workingIds.length === 0
|
|
690
|
+
? "No other tracked subsessions are working."
|
|
691
|
+
: `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.`;
|
|
692
|
+
const text = `Subsession ${childId} stopped working (${status}).\n${next}\n\n--- SUBSESSION OUTPUT: ${childId} ---\n${preview}`;
|
|
695
693
|
void this.notifyParentOfSubsession(link.parentSessionId, childId, text);
|
|
696
694
|
}
|
|
697
695
|
async getOrOpenParentForSubsession(parentSessionId, childSessionId) {
|
|
@@ -736,21 +734,21 @@ export class PiSessionService {
|
|
|
736
734
|
}
|
|
737
735
|
async availableModels(ref, managementContext) {
|
|
738
736
|
const session = await this.getOrOpen(ref, managementContext);
|
|
739
|
-
session.
|
|
737
|
+
await session.modelRuntime.reloadConfig();
|
|
740
738
|
const models = session.scopedModels.length > 0
|
|
741
739
|
? session.scopedModels.map((scoped) => scoped.model)
|
|
742
|
-
: session.
|
|
740
|
+
: await session.modelRuntime.getAvailable();
|
|
743
741
|
return models.map(modelToClientModel);
|
|
744
742
|
}
|
|
745
743
|
async setModel(ref, provider, modelId, managementContext) {
|
|
746
744
|
await this.assertWritable(ref);
|
|
747
745
|
const session = await this.getOrOpen(ref, managementContext);
|
|
748
|
-
session.
|
|
746
|
+
await session.modelRuntime.reloadConfig();
|
|
749
747
|
const candidates = session.scopedModels.length > 0
|
|
750
748
|
? session.scopedModels.map((scoped) => scoped.model)
|
|
751
|
-
: session.
|
|
749
|
+
: await session.modelRuntime.getAvailable();
|
|
752
750
|
const model = candidates.find((candidate) => candidate.provider === provider && candidate.id === modelId)
|
|
753
|
-
?? session.
|
|
751
|
+
?? session.modelRuntime.getModel(provider, modelId);
|
|
754
752
|
if (model === undefined)
|
|
755
753
|
throw new Error(`未找到模型:${provider}/${modelId}`);
|
|
756
754
|
await session.setModel(model);
|
|
@@ -1121,6 +1119,14 @@ export class PiSessionService {
|
|
|
1121
1119
|
this.publishActivity(active.runtime.session, "stopped", "idle");
|
|
1122
1120
|
this.publishStatus(active.runtime.session);
|
|
1123
1121
|
}
|
|
1122
|
+
async clearQueue(ref, managementContext) {
|
|
1123
|
+
await this.assertWritable(ref);
|
|
1124
|
+
const session = await this.getOrOpen(ref, managementContext);
|
|
1125
|
+
this.clearCompactionPromptQueue(session.sessionId);
|
|
1126
|
+
clearSessionQueue(session);
|
|
1127
|
+
this.publishStatus(session);
|
|
1128
|
+
return this.statusFromSession(session);
|
|
1129
|
+
}
|
|
1124
1130
|
stop(ref, managementContext) {
|
|
1125
1131
|
const active = this.activeForLookup(ref, managementContext);
|
|
1126
1132
|
if (active === undefined)
|
|
@@ -1316,6 +1322,9 @@ export class PiSessionService {
|
|
|
1316
1322
|
return [...names];
|
|
1317
1323
|
}
|
|
1318
1324
|
async closeActive(sessionId) {
|
|
1325
|
+
const pendingOpens = this.pendingSessionOpenPromises(sessionId);
|
|
1326
|
+
if (pendingOpens.length > 0)
|
|
1327
|
+
await Promise.allSettled(pendingOpens);
|
|
1319
1328
|
const activeSessions = this.activeSessionsForId(sessionId);
|
|
1320
1329
|
await Promise.all(activeSessions.map((active) => this.closeActiveSession(active)));
|
|
1321
1330
|
}
|
|
@@ -1358,14 +1367,36 @@ export class PiSessionService {
|
|
|
1358
1367
|
return active;
|
|
1359
1368
|
const archived = await this.getArchived(ref);
|
|
1360
1369
|
if (archived?.archivePath !== undefined) {
|
|
1361
|
-
|
|
1370
|
+
const archivePath = archived.archivePath;
|
|
1371
|
+
return this.openPending(archived.sessionId, archived.cwd, managementContext, () => this.sessionManager.open(archivePath));
|
|
1362
1372
|
}
|
|
1363
1373
|
const match = isPiSessionRef(ref)
|
|
1364
1374
|
? (await this.sessionManager.list(ref.cwd)).find((s) => s.id === ref.id || s.id.startsWith(ref.id))
|
|
1365
1375
|
: (await this.sessionManager.listAll?.() ?? []).find((s) => s.id === ref || s.id.startsWith(ref));
|
|
1366
1376
|
if (!match)
|
|
1367
1377
|
throw new Error("未找到会话");
|
|
1368
|
-
return this.
|
|
1378
|
+
return this.openPending(match.id, match.cwd, managementContext, () => this.sessionManager.open(match.path));
|
|
1379
|
+
}
|
|
1380
|
+
openPending(sessionId, cwd, managementContext, openSessionManager) {
|
|
1381
|
+
const key = JSON.stringify([canonicalizeStoredCwd(cwd), sessionId, managementContextKey(managementContext)]);
|
|
1382
|
+
const existing = this.pendingSessionOpens.get(key);
|
|
1383
|
+
if (existing !== undefined)
|
|
1384
|
+
return existing.promise;
|
|
1385
|
+
const pending = {
|
|
1386
|
+
sessionId,
|
|
1387
|
+
promise: this.create(openSessionManager(), cwd, managementContext === undefined ? {} : { managementContext }),
|
|
1388
|
+
};
|
|
1389
|
+
pending.promise = pending.promise.finally(() => {
|
|
1390
|
+
if (this.pendingSessionOpens.get(key) === pending)
|
|
1391
|
+
this.pendingSessionOpens.delete(key);
|
|
1392
|
+
});
|
|
1393
|
+
this.pendingSessionOpens.set(key, pending);
|
|
1394
|
+
return pending.promise;
|
|
1395
|
+
}
|
|
1396
|
+
pendingSessionOpenPromises(sessionId) {
|
|
1397
|
+
return [...this.pendingSessionOpens.values()]
|
|
1398
|
+
.filter((pending) => sessionId === undefined || pending.sessionId === sessionId)
|
|
1399
|
+
.map((pending) => pending.promise);
|
|
1369
1400
|
}
|
|
1370
1401
|
async getArchived(ref) {
|
|
1371
1402
|
const archived = await this.archiveStore.get(sessionIdFromLookup(ref));
|
|
@@ -1425,25 +1456,53 @@ export class PiSessionService {
|
|
|
1425
1456
|
return [...new Set(this.active.values())].filter((active) => active.runtime.session.sessionId === sessionId);
|
|
1426
1457
|
}
|
|
1427
1458
|
async create(sessionManager, cwd, options = {}) {
|
|
1459
|
+
const delegationToolsEnabled = options.creationProvenance !== "tracked-subsession"
|
|
1460
|
+
&& await sessionAllowsDelegationTools(sessionManager, this.sessionManager);
|
|
1428
1461
|
const runtime = await this.createAgentRuntime(this.createRuntime, {
|
|
1429
1462
|
cwd,
|
|
1430
1463
|
agentDir: this.agentDir,
|
|
1431
1464
|
sessionManager,
|
|
1465
|
+
delegationToolsEnabled,
|
|
1432
1466
|
...(options.managementContext === undefined ? {} : { managementContext: options.managementContext }),
|
|
1433
1467
|
...(options.initialModel === undefined ? {} : { initialModel: options.initialModel }),
|
|
1434
1468
|
});
|
|
1435
1469
|
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);
|
|
1470
|
+
try {
|
|
1471
|
+
await this.bindSessionExtensions(runtime.session);
|
|
1441
1472
|
this.bindRuntime(active);
|
|
1442
|
-
|
|
1443
|
-
|
|
1444
|
-
|
|
1445
|
-
|
|
1446
|
-
|
|
1473
|
+
runtime.setRebindSession(async (session) => {
|
|
1474
|
+
await this.bindSessionExtensions(session);
|
|
1475
|
+
this.bindRuntime(active);
|
|
1476
|
+
await this.recoverSubsessionTrackingForOpenedSession(session);
|
|
1477
|
+
});
|
|
1478
|
+
this.active.set(activeSessionKey(runtime.session.sessionId, active.eventScope), active);
|
|
1479
|
+
await this.recoverSubsessionTrackingForOpenedSession(runtime.session);
|
|
1480
|
+
this.publishStatus(runtime.session);
|
|
1481
|
+
return active;
|
|
1482
|
+
}
|
|
1483
|
+
catch (error) {
|
|
1484
|
+
active.unsubscribe();
|
|
1485
|
+
let removedActive = false;
|
|
1486
|
+
for (const [sessionKey, candidate] of this.active.entries()) {
|
|
1487
|
+
if (candidate !== active)
|
|
1488
|
+
continue;
|
|
1489
|
+
this.active.delete(sessionKey);
|
|
1490
|
+
this.activities.delete(sessionKey);
|
|
1491
|
+
removedActive = true;
|
|
1492
|
+
}
|
|
1493
|
+
this.clearAuthLossWarningsForSession(runtime.session.sessionId);
|
|
1494
|
+
this.clearCompactionPromptQueue(runtime.session.sessionId);
|
|
1495
|
+
if (removedActive) {
|
|
1496
|
+
this.workspaceActivity?.removeSession(runtime.session.sessionId, runtime.session.sessionManager.getCwd(), active.eventScope);
|
|
1497
|
+
}
|
|
1498
|
+
try {
|
|
1499
|
+
await runtime.session.abort();
|
|
1500
|
+
}
|
|
1501
|
+
finally {
|
|
1502
|
+
await runtime.dispose();
|
|
1503
|
+
}
|
|
1504
|
+
throw error;
|
|
1505
|
+
}
|
|
1447
1506
|
}
|
|
1448
1507
|
async bindSessionExtensions(session) {
|
|
1449
1508
|
await session.bindExtensions({
|
|
@@ -1559,15 +1618,12 @@ export class PiSessionService {
|
|
|
1559
1618
|
session.setSessionName(name);
|
|
1560
1619
|
this.publishSessionName(session);
|
|
1561
1620
|
}
|
|
1562
|
-
applyAuthChange(change
|
|
1563
|
-
|
|
1564
|
-
this.managementModelRegistry.refresh();
|
|
1565
|
-
const changedScope = change.scope ?? "normal";
|
|
1621
|
+
applyAuthChange(change) {
|
|
1622
|
+
const changedRuntime = change.modelRuntime;
|
|
1566
1623
|
for (const active of this.active.values()) {
|
|
1567
|
-
if (authScopeFromEventScope(active.eventScope) !== changedScope)
|
|
1568
|
-
continue;
|
|
1569
1624
|
const { session } = active.runtime;
|
|
1570
|
-
session.
|
|
1625
|
+
if (session.modelRuntime !== changedRuntime)
|
|
1626
|
+
continue;
|
|
1571
1627
|
this.syncCurrentModelAuthWarning(session, active.eventScope, change.removedProviderId);
|
|
1572
1628
|
this.publishStatus(session);
|
|
1573
1629
|
}
|
|
@@ -1579,10 +1635,10 @@ export class PiSessionService {
|
|
|
1579
1635
|
if (model.provider === "unknown" && model.id === "unknown")
|
|
1580
1636
|
return;
|
|
1581
1637
|
const warningKey = authLossWarningKey(session.sessionId, eventScope, model.provider, model.id);
|
|
1582
|
-
const registered = session.
|
|
1638
|
+
const registered = session.modelRuntime.getModel(model.provider, model.id);
|
|
1583
1639
|
if (registered === undefined)
|
|
1584
1640
|
return;
|
|
1585
|
-
if (session.
|
|
1641
|
+
if (session.modelRuntime.hasConfiguredAuth(registered.provider)) {
|
|
1586
1642
|
this.authLossWarnings.delete(warningKey);
|
|
1587
1643
|
return;
|
|
1588
1644
|
}
|
|
@@ -1847,6 +1903,76 @@ function addSessionName(names, name) {
|
|
|
1847
1903
|
function isDefined(value) {
|
|
1848
1904
|
return value !== undefined;
|
|
1849
1905
|
}
|
|
1906
|
+
/**
|
|
1907
|
+
* Resolve the delegation capability from server-owned, persisted session
|
|
1908
|
+
* provenance. A copied marker is not enough: the child header and reciprocal
|
|
1909
|
+
* parent link must identify the exact same session files.
|
|
1910
|
+
*/
|
|
1911
|
+
export async function sessionAllowsDelegationTools(sessionManager, managers) {
|
|
1912
|
+
const trackedLink = await verifiedTrackedSubsessionLink(managers, {
|
|
1913
|
+
sessionId: sessionManager.getSessionId(),
|
|
1914
|
+
sessionFile: sessionManager.getSessionFile(),
|
|
1915
|
+
sessionManager,
|
|
1916
|
+
cwd: sessionManager.getCwd(),
|
|
1917
|
+
});
|
|
1918
|
+
return trackedLink === undefined;
|
|
1919
|
+
}
|
|
1920
|
+
async function verifiedTrackedSubsessionLink(managers, session) {
|
|
1921
|
+
// Child markers are only hints; the current child header and reciprocal
|
|
1922
|
+
// parent custom link must agree on the exact ids and files before relinking.
|
|
1923
|
+
const entries = session.sessionManager.getEntries?.() ?? session.sessionManager.getBranch();
|
|
1924
|
+
let marker;
|
|
1925
|
+
for (const entry of entries) {
|
|
1926
|
+
const parsed = parsePersistedChildSubsessionLink(entry);
|
|
1927
|
+
if (parsed?.spawnedSessionId === session.sessionId)
|
|
1928
|
+
marker = parsed;
|
|
1929
|
+
}
|
|
1930
|
+
if (marker === undefined)
|
|
1931
|
+
return undefined;
|
|
1932
|
+
const childSessionFile = nonEmptyString(session.sessionFile);
|
|
1933
|
+
if (childSessionFile === undefined)
|
|
1934
|
+
return undefined;
|
|
1935
|
+
const childHeader = await readSessionHeaderSummary(childSessionFile);
|
|
1936
|
+
if (childHeader?.id !== session.sessionId)
|
|
1937
|
+
return undefined;
|
|
1938
|
+
const parentSessionFile = nonEmptyString(childHeader.parentSession);
|
|
1939
|
+
if (parentSessionFile === undefined)
|
|
1940
|
+
return undefined;
|
|
1941
|
+
const parentHeader = await readSessionHeaderSummary(parentSessionFile);
|
|
1942
|
+
if (parentHeader?.id !== marker.spawnedBySessionId)
|
|
1943
|
+
return undefined;
|
|
1944
|
+
const parentLink = findReciprocalParentSubsessionLink(managers, parentSessionFile, marker.spawnedBySessionId, session.sessionId, childSessionFile);
|
|
1945
|
+
if (parentLink === undefined)
|
|
1946
|
+
return undefined;
|
|
1947
|
+
return {
|
|
1948
|
+
parentSessionId: marker.spawnedBySessionId,
|
|
1949
|
+
childSessionId: session.sessionId,
|
|
1950
|
+
childSessionFile,
|
|
1951
|
+
parentSessionFile,
|
|
1952
|
+
cwd: parentLink.cwd ?? session.cwd,
|
|
1953
|
+
};
|
|
1954
|
+
}
|
|
1955
|
+
function findReciprocalParentSubsessionLink(managers, parentSessionFile, parentSessionId, childSessionId, childSessionFile) {
|
|
1956
|
+
let parentManager;
|
|
1957
|
+
try {
|
|
1958
|
+
parentManager = managers.open(parentSessionFile);
|
|
1959
|
+
}
|
|
1960
|
+
catch {
|
|
1961
|
+
return undefined;
|
|
1962
|
+
}
|
|
1963
|
+
const entries = parentManager.getEntries?.() ?? parentManager.getBranch();
|
|
1964
|
+
for (const entry of entries) {
|
|
1965
|
+
const link = parsePersistedParentSubsessionLink(entry);
|
|
1966
|
+
if (link === undefined)
|
|
1967
|
+
continue;
|
|
1968
|
+
if (link.spawnedBySessionId !== parentSessionId || link.spawnedSessionId !== childSessionId)
|
|
1969
|
+
continue;
|
|
1970
|
+
if (link.spawnedSessionFile === undefined || !sessionPathsEqual(link.spawnedSessionFile, childSessionFile))
|
|
1971
|
+
continue;
|
|
1972
|
+
return link;
|
|
1973
|
+
}
|
|
1974
|
+
return undefined;
|
|
1975
|
+
}
|
|
1850
1976
|
function trackedSubsessionLinkFromParentLink(parentSessionId, link, parentSessionFile) {
|
|
1851
1977
|
return {
|
|
1852
1978
|
parentSessionId,
|