@jmfederico/pi-web 1.202607.0 → 1.202607.1
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 +5 -2
- package/dist/cli.js +291 -320
- package/dist/cli.js.map +1 -1
- package/dist/client/assets/{CodeViewer-tXF3UTsY.js → CodeViewer-uASb28Y1.js} +1 -1
- package/dist/client/assets/{TerminalPanel-D_ihiWAt.js → TerminalPanel-DfTmykFw.js} +1 -1
- package/dist/client/assets/{UnifiedDiffViewer-BziirXBJ.js → UnifiedDiffViewer-laQN1x7P.js} +1 -1
- package/dist/client/assets/{index-Brfk1lwl.js → index-Dhapi0oR.js} +396 -253
- package/dist/client/index.html +6 -6
- package/dist/client/manifest.webmanifest +4 -4
- package/dist/config.js +144 -2
- 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 +358 -0
- package/dist/nativeServices/servicePlan.js.map +1 -0
- package/dist/nativeServices/serviceProbe.js +447 -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 +12 -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 +57 -16
- package/dist/server/app.js.map +1 -1
- package/dist/server/configRoutes.js +43 -9
- package/dist/server/configRoutes.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/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 +33 -37
- 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 +33 -7
- package/dist/server/realtime/sessionEventHub.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 +88 -62
- package/dist/server/sessiond.js.map +1 -1
- package/dist/server/sessions/authProviderOptions.js +30 -32
- package/dist/server/sessions/authProviderOptions.js.map +1 -1
- package/dist/server/sessions/authRoutes.js +14 -4
- package/dist/server/sessions/authRoutes.js.map +1 -1
- package/dist/server/sessions/authService.js +89 -29
- package/dist/server/sessions/authService.js.map +1 -1
- package/dist/server/sessions/oauthLoginFlowService.js +176 -53
- 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 +214 -25
- package/dist/server/sessions/piSessionService.js.map +1 -1
- 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 +27 -2
- 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 +66 -26
- package/dist/server/sessions/spawnSubsessionTool.js.map +1 -1
- package/dist/server/terminals/terminalService.js +10 -4
- 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 +97 -2
- 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 +4 -0
- package/dist/shared/federatedRoutes.js.map +1 -1
- package/dist/shared/piWebStatusParsing.js +6 -0
- package/dist/shared/piWebStatusParsing.js.map +1 -1
- package/docs/config.md +56 -11
- package/docs/plugins.md +19 -5
- package/package.json +8 -8
|
@@ -1,37 +1,38 @@
|
|
|
1
1
|
import { readdir } from "node:fs/promises";
|
|
2
2
|
import { homedir } from "node:os";
|
|
3
3
|
import { dirname, isAbsolute, join, resolve } from "node:path";
|
|
4
|
-
import {
|
|
4
|
+
import { SessionManager, SettingsManager } from "@earendil-works/pi-coding-agent";
|
|
5
5
|
import { canonicalizeStoredCwd, cwdPathsEqual } from "../workingDirectory.js";
|
|
6
|
-
export const PI_SESSION_DIR_ENV = "PI_CODING_AGENT_SESSION_DIR";
|
|
7
6
|
export class SessionDirResolver {
|
|
8
|
-
constructor(options
|
|
9
|
-
this.agentDir = options.agentDir
|
|
10
|
-
this.
|
|
7
|
+
constructor(options) {
|
|
8
|
+
this.agentDir = options.agentDir;
|
|
9
|
+
this.envSessionDir = options.sessionDirEnvKeys
|
|
10
|
+
.map((key) => options.env[key])
|
|
11
|
+
.find((value) => value !== undefined && value !== "");
|
|
12
|
+
const configuredHome = options.env["HOME"];
|
|
13
|
+
this.homeDir = configuredHome !== undefined && configuredHome !== "" && isAbsolute(configuredHome) ? configuredHome : homedir();
|
|
11
14
|
}
|
|
12
15
|
defaultSessionsRoot() {
|
|
13
16
|
return defaultPiSessionsRoot(this.agentDir);
|
|
14
17
|
}
|
|
15
18
|
globalEnvSessionDir() {
|
|
16
|
-
|
|
17
|
-
if (envSessionDir === undefined || envSessionDir === "")
|
|
19
|
+
if (this.envSessionDir === undefined)
|
|
18
20
|
return undefined;
|
|
19
|
-
const expanded = expandTildePath(envSessionDir);
|
|
21
|
+
const expanded = expandTildePath(this.envSessionDir, this.homeDir);
|
|
20
22
|
return isAbsolute(expanded) ? expanded : undefined;
|
|
21
23
|
}
|
|
22
24
|
resolve(cwd) {
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
return { source: "env", sessionDir: resolveConfiguredPath(envSessionDir, cwd), usesConfiguredSessionDir: true };
|
|
25
|
+
if (this.envSessionDir !== undefined) {
|
|
26
|
+
return { source: "env", sessionDir: resolveConfiguredPath(this.envSessionDir, cwd, this.homeDir), usesConfiguredSessionDir: true };
|
|
26
27
|
}
|
|
27
28
|
const settingsSessionDir = SettingsManager.create(cwd, this.agentDir).getSessionDir();
|
|
28
29
|
if (settingsSessionDir !== undefined && settingsSessionDir !== "") {
|
|
29
|
-
return { source: "settings", sessionDir: resolveConfiguredPath(settingsSessionDir, cwd), usesConfiguredSessionDir: true };
|
|
30
|
+
return { source: "settings", sessionDir: resolveConfiguredPath(settingsSessionDir, cwd, this.homeDir), usesConfiguredSessionDir: true };
|
|
30
31
|
}
|
|
31
32
|
return { source: "pi-default", sessionDir: defaultPiSessionDir(cwd, this.agentDir), usesConfiguredSessionDir: false };
|
|
32
33
|
}
|
|
33
34
|
}
|
|
34
|
-
export function createPiSessionManagerGateway(options
|
|
35
|
+
export function createPiSessionManagerGateway(options) {
|
|
35
36
|
return new SettingsAwarePiSessionManagerGateway(new SessionDirResolver(options));
|
|
36
37
|
}
|
|
37
38
|
class SettingsAwarePiSessionManagerGateway {
|
|
@@ -67,7 +68,7 @@ export async function listSessionsInDir(sessionDir) {
|
|
|
67
68
|
const sessions = await SessionManager.listAll(sessionDir);
|
|
68
69
|
return sessions.map((session) => ({ ...session, cwd: canonicalizeStoredCwd(session.cwd) }));
|
|
69
70
|
}
|
|
70
|
-
export async function listSessionsInDefaultPiStore(storeRoot
|
|
71
|
+
export async function listSessionsInDefaultPiStore(storeRoot) {
|
|
71
72
|
let entries;
|
|
72
73
|
try {
|
|
73
74
|
entries = await readdir(storeRoot, { withFileTypes: true });
|
|
@@ -90,25 +91,25 @@ function uniqueSessionsByPath(sessions) {
|
|
|
90
91
|
byPath.set(session.path, session);
|
|
91
92
|
return [...byPath.values()].sort((a, b) => b.modified.getTime() - a.modified.getTime());
|
|
92
93
|
}
|
|
93
|
-
export function defaultPiSessionsRoot(agentDir
|
|
94
|
+
export function defaultPiSessionsRoot(agentDir) {
|
|
94
95
|
return join(agentDir, "sessions");
|
|
95
96
|
}
|
|
96
|
-
export function defaultPiSessionDir(cwd, agentDir
|
|
97
|
+
export function defaultPiSessionDir(cwd, agentDir) {
|
|
97
98
|
return sessionDirInDefaultPiStore(defaultPiSessionsRoot(agentDir), cwd);
|
|
98
99
|
}
|
|
99
100
|
export function sessionDirInDefaultPiStore(storeRoot, cwd) {
|
|
100
101
|
const safePath = `--${cwd.replace(/^[/\\]/u, "").replace(/[/\\:]/gu, "-")}--`;
|
|
101
102
|
return join(storeRoot, safePath);
|
|
102
103
|
}
|
|
103
|
-
export function resolveConfiguredPath(path, cwd) {
|
|
104
|
-
const expanded = expandTildePath(path);
|
|
104
|
+
export function resolveConfiguredPath(path, cwd, homeDir) {
|
|
105
|
+
const expanded = expandTildePath(path, homeDir);
|
|
105
106
|
return isAbsolute(expanded) ? expanded : resolve(cwd, expanded);
|
|
106
107
|
}
|
|
107
|
-
function expandTildePath(path) {
|
|
108
|
+
function expandTildePath(path, homeDir) {
|
|
108
109
|
if (path === "~")
|
|
109
|
-
return
|
|
110
|
+
return homeDir;
|
|
110
111
|
if (path.startsWith("~/"))
|
|
111
|
-
return join(
|
|
112
|
+
return join(homeDir, path.slice(2));
|
|
112
113
|
return path;
|
|
113
114
|
}
|
|
114
115
|
//# sourceMappingURL=piSessionManagerGateway.js.map
|
|
@@ -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;AAC/D,OAAO,EAAE,
|
|
1
|
+
{"version":3,"file":"piSessionManagerGateway.js","sourceRoot":"","sources":["../../../src/server/sessions/piSessionManagerGateway.ts"],"names":[],"mappings":"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"}
|
|
@@ -1,6 +1,8 @@
|
|
|
1
1
|
import { statSync } from "node:fs";
|
|
2
|
+
import { join } from "node:path";
|
|
2
3
|
import { open, readFile, writeFile } from "node:fs/promises";
|
|
3
|
-
import {
|
|
4
|
+
import { createAgentSessionFromServices, createAgentSessionRuntime, createAgentSessionServices, createEditToolDefinition, defineTool, readStoredCredential, SessionManager, } from "@earendil-works/pi-coding-agent";
|
|
5
|
+
import { projectBrowserMessage } from "../browserMessageProjection.js";
|
|
4
6
|
import { pageMessagesAtSafeBoundary } from "./messagePaging.js";
|
|
5
7
|
import { BUILTIN_COMMANDS } from "./builtinCommands.js";
|
|
6
8
|
import { SessionCommandService } from "./sessionCommandService.js";
|
|
@@ -8,7 +10,6 @@ import { SessionArchiveStore } from "./sessionArchiveStore.js";
|
|
|
8
10
|
import { findArchiveCandidateByIdOrPrefix, planSessionArchiveTree } from "./sessionArchiveTree.js";
|
|
9
11
|
import { deterministicSessionName, fallbackSessionName, generateShortSessionName } from "./sessionNameGenerator.js";
|
|
10
12
|
import { computeEditPreview } from "./editPreview.js";
|
|
11
|
-
import { createPiSessionManagerGateway } from "./piSessionManagerGateway.js";
|
|
12
13
|
import { attachmentsToInlineImages, saveAttachmentsToWorkspace } from "./attachmentService.js";
|
|
13
14
|
import { parsePromptAttachments } from "../../shared/promptAttachments.js";
|
|
14
15
|
import { canonicalizeStoredCwd, cwdPathsEqual } from "../workingDirectory.js";
|
|
@@ -49,6 +50,107 @@ function parsePromptStreamingBehavior(value) {
|
|
|
49
50
|
return value;
|
|
50
51
|
throw new Error('Prompt streamingBehavior must be "steer" or "followUp"');
|
|
51
52
|
}
|
|
53
|
+
function resourceDiagnosticToWarning(diagnostic, source) {
|
|
54
|
+
return {
|
|
55
|
+
severity: diagnostic.type === "error" ? "error" : "warning",
|
|
56
|
+
message: diagnostic.message,
|
|
57
|
+
source,
|
|
58
|
+
...(diagnostic.path === undefined ? {} : { path: diagnostic.path }),
|
|
59
|
+
};
|
|
60
|
+
}
|
|
61
|
+
function runtimeDiagnosticToWarning(diagnostic) {
|
|
62
|
+
return { severity: diagnostic.type, message: diagnostic.message, source: "runtime" };
|
|
63
|
+
}
|
|
64
|
+
/**
|
|
65
|
+
* Compute the live warnings for a runtime by re-reading its current resource
|
|
66
|
+
* loader diagnostics, extension load errors, and runtime setup diagnostics.
|
|
67
|
+
*
|
|
68
|
+
* This mimics the TUI recomputing warnings on every (re)bind: it reads the
|
|
69
|
+
* runtime's current state rather than a cached snapshot, so a rebuilt runtime
|
|
70
|
+
* yields fresh warnings. Runtimes without SDK services (e.g. test fakes)
|
|
71
|
+
* contribute no warnings.
|
|
72
|
+
*/
|
|
73
|
+
export function collectRuntimeWarnings(runtime) {
|
|
74
|
+
const warnings = [];
|
|
75
|
+
for (const diagnostic of runtime.diagnostics ?? [])
|
|
76
|
+
warnings.push(runtimeDiagnosticToWarning(diagnostic));
|
|
77
|
+
const resourceLoader = runtime.services?.resourceLoader;
|
|
78
|
+
if (resourceLoader !== undefined) {
|
|
79
|
+
for (const diagnostic of resourceLoader.getSkills().diagnostics)
|
|
80
|
+
warnings.push(resourceDiagnosticToWarning(diagnostic, "skill"));
|
|
81
|
+
for (const diagnostic of resourceLoader.getPrompts().diagnostics)
|
|
82
|
+
warnings.push(resourceDiagnosticToWarning(diagnostic, "prompt"));
|
|
83
|
+
for (const diagnostic of resourceLoader.getThemes().diagnostics)
|
|
84
|
+
warnings.push(resourceDiagnosticToWarning(diagnostic, "theme"));
|
|
85
|
+
for (const error of resourceLoader.getExtensions().errors) {
|
|
86
|
+
warnings.push({ severity: "error", message: `${error.path}: ${error.error}`, source: "extension", path: error.path });
|
|
87
|
+
}
|
|
88
|
+
}
|
|
89
|
+
return warnings;
|
|
90
|
+
}
|
|
91
|
+
/**
|
|
92
|
+
* Verbatim TUI wording for the Anthropic subscription-auth billing notice. Kept
|
|
93
|
+
* character-for-character in sync with `ANTHROPIC_SUBSCRIPTION_AUTH_WARNING` in
|
|
94
|
+
* the SDK's interactive mode so the browser shows the same message the TUI does.
|
|
95
|
+
*/
|
|
96
|
+
const ANTHROPIC_SUBSCRIPTION_AUTH_WARNING = "Anthropic subscription auth is active. Third-party harness usage draws from extra usage and is billed per token, not your Claude plan limits. Manage extra usage at https://claude.ai/settings/usage.";
|
|
97
|
+
/** Mirror of the SDK TUI `isAnthropicSubscriptionAuthKey` (subscription API keys start with `sk-ant-oat`). */
|
|
98
|
+
function isAnthropicSubscriptionAuthKey(apiKey) {
|
|
99
|
+
return typeof apiKey === "string" && apiKey.startsWith("sk-ant-oat");
|
|
100
|
+
}
|
|
101
|
+
/**
|
|
102
|
+
* Dismiss id for the Anthropic subscription-auth billing notice. This is `pi`'s
|
|
103
|
+
* own `WarningSettings` key verbatim (`anthropicExtraUsage`): we carry the
|
|
104
|
+
* coupling `pi` already defines rather than inventing a parallel vocabulary, and
|
|
105
|
+
* {@link dismissSessionWarning} maps it back to `setWarnings`.
|
|
106
|
+
*/
|
|
107
|
+
const ANTHROPIC_EXTRA_USAGE_DISMISS_ID = "anthropicExtraUsage";
|
|
108
|
+
/**
|
|
109
|
+
* Port of the TUI `maybeWarnAboutAnthropicSubscriptionAuth` gate/trigger, computed
|
|
110
|
+
* live from the session's current model, stored Anthropic credential, and warning
|
|
111
|
+
* settings. Returns the billing warning when the active provider is `anthropic`
|
|
112
|
+
* and auth is a subscription credential (stored `oauth`, or an `sk-ant-oat` API
|
|
113
|
+
* key), unless suppressed via `getWarnings().anthropicExtraUsage === false`.
|
|
114
|
+
*
|
|
115
|
+
* The stored credential is read synchronously (matching the TUI's `oauth` branch
|
|
116
|
+
* and the documented `sk-ant-oat` key trigger) so warnings stay part of the
|
|
117
|
+
* synchronous live status computation.
|
|
118
|
+
*/
|
|
119
|
+
export function anthropicSubscriptionWarning(session, authPath) {
|
|
120
|
+
if (session.settingsManager.getWarnings().anthropicExtraUsage === false)
|
|
121
|
+
return undefined;
|
|
122
|
+
if (session.model?.provider !== "anthropic")
|
|
123
|
+
return undefined;
|
|
124
|
+
const credential = readStoredCredential("anthropic", authPath);
|
|
125
|
+
if (credential === undefined)
|
|
126
|
+
return undefined;
|
|
127
|
+
const isSubscriptionAuth = credential.type === "oauth"
|
|
128
|
+
? true
|
|
129
|
+
: isAnthropicSubscriptionAuthKey(credential.key);
|
|
130
|
+
if (!isSubscriptionAuth)
|
|
131
|
+
return undefined;
|
|
132
|
+
return {
|
|
133
|
+
severity: "warning",
|
|
134
|
+
message: ANTHROPIC_SUBSCRIPTION_AUTH_WARNING,
|
|
135
|
+
source: "anthropic",
|
|
136
|
+
dismiss: { id: ANTHROPIC_EXTRA_USAGE_DISMISS_ID },
|
|
137
|
+
};
|
|
138
|
+
}
|
|
139
|
+
/**
|
|
140
|
+
* Durably suppress a dismissable session warning by mapping its opaque dismiss
|
|
141
|
+
* id back to the concrete `pi` suppression it represents. Only known ids are
|
|
142
|
+
* honored; unknown ids throw so a stale/forged client cannot silently no-op.
|
|
143
|
+
*
|
|
144
|
+
* This is the single place provider-specific suppression lives: the wire type,
|
|
145
|
+
* parser, and UI stay agnostic. Adding a future dismissable warning is a
|
|
146
|
+
* server-only change here plus a `dismiss` id on its producer.
|
|
147
|
+
*/
|
|
148
|
+
export function dismissSessionWarning(session, dismissId) {
|
|
149
|
+
if (dismissId !== ANTHROPIC_EXTRA_USAGE_DISMISS_ID) {
|
|
150
|
+
throw new Error(`Unknown session warning dismiss id: ${dismissId}`);
|
|
151
|
+
}
|
|
152
|
+
session.settingsManager.setWarnings({ ...session.settingsManager.getWarnings(), anthropicExtraUsage: false });
|
|
153
|
+
}
|
|
52
154
|
function defaultCreateAgentRuntime(createRuntime, options) {
|
|
53
155
|
if (!(options.sessionManager instanceof SessionManager))
|
|
54
156
|
throw new Error("Default runtime creation requires an SDK SessionManager");
|
|
@@ -83,9 +185,9 @@ export function createPiWebCustomToolDefinitions(cwd, delegationEnabled, spawn,
|
|
|
83
185
|
...(delegationEnabled && subsessions !== undefined ? createSubsessionToolDefinitions(cwd, subsessions) : []),
|
|
84
186
|
];
|
|
85
187
|
}
|
|
86
|
-
function createDefaultRuntimeFactory(
|
|
188
|
+
function createDefaultRuntimeFactory(modelRuntime, sessionManagers, spawn, subsessions) {
|
|
87
189
|
return async ({ cwd, agentDir, sessionManager, sessionStartEvent, initialModel, delegationToolsEnabled }) => {
|
|
88
|
-
const services = await createAgentSessionServices({ cwd, agentDir,
|
|
190
|
+
const services = await createAgentSessionServices({ cwd, agentDir, modelRuntime });
|
|
89
191
|
const resolvedDelegationToolsEnabled = delegationToolsEnabled
|
|
90
192
|
?? await sessionAllowsDelegationTools(sessionManager, sessionManagers);
|
|
91
193
|
const customTools = createPiWebCustomToolDefinitions(cwd, resolvedDelegationToolsEnabled, spawn, subsessions);
|
|
@@ -121,7 +223,7 @@ function createPiWebEditToolDefinition(cwd) {
|
|
|
121
223
|
});
|
|
122
224
|
}
|
|
123
225
|
export class PiSessionService {
|
|
124
|
-
constructor(events, deps
|
|
226
|
+
constructor(events, deps) {
|
|
125
227
|
this.events = events;
|
|
126
228
|
this.active = new Map();
|
|
127
229
|
this.pendingSessionOpens = new Map();
|
|
@@ -144,16 +246,16 @@ export class PiSessionService {
|
|
|
144
246
|
*/
|
|
145
247
|
this.subsessionNotifyArmed = new Map();
|
|
146
248
|
this.archiveStore = deps.archiveStore ?? new SessionArchiveStore();
|
|
147
|
-
this.agentDir = deps.agentDir
|
|
148
|
-
this.sessionManager = deps.sessionManager
|
|
149
|
-
this.
|
|
249
|
+
this.agentDir = deps.agentDir;
|
|
250
|
+
this.sessionManager = deps.sessionManager;
|
|
251
|
+
this.modelRuntime = deps.modelRuntime;
|
|
150
252
|
this.spawnTargets = deps.spawnTargets;
|
|
151
253
|
this.logger = deps.logger ?? noopLogger;
|
|
152
254
|
this.now = deps.now ?? (() => new Date());
|
|
153
255
|
// Subsessions are a beta capability gated behind their own flag, and they
|
|
154
256
|
// also require the spawn capability (they share its project-scope resolver).
|
|
155
257
|
const subsessionsActive = this.spawnTargets !== undefined && deps.subsessionsEnabled === true;
|
|
156
|
-
this.createRuntime = deps.createRuntime ?? createDefaultRuntimeFactory(this.
|
|
258
|
+
this.createRuntime = deps.createRuntime ?? createDefaultRuntimeFactory(this.modelRuntime, this.sessionManager, this.spawnTargets === undefined ? undefined : (input) => this.spawnSession(input), !subsessionsActive ? undefined : {
|
|
157
259
|
spawn: (input) => this.spawnSubsession(input),
|
|
158
260
|
list: (parentSessionId, parentSessionFile) => this.listSubsessions(parentSessionId, parentSessionFile),
|
|
159
261
|
check: (parentSessionId, sessionId, parentSessionFile) => this.checkSubsession(parentSessionId, sessionId, parentSessionFile),
|
|
@@ -563,6 +665,16 @@ export class PiSessionService {
|
|
|
563
665
|
return "error";
|
|
564
666
|
return "idle";
|
|
565
667
|
}
|
|
668
|
+
workingSubsessionIds(parentSessionId) {
|
|
669
|
+
const childIds = this.subsessionChildren.get(parentSessionId);
|
|
670
|
+
if (childIds === undefined)
|
|
671
|
+
return [];
|
|
672
|
+
return [...childIds].filter((childId) => {
|
|
673
|
+
const link = this.subsessionLinks.get(childId);
|
|
674
|
+
const active = link === undefined ? undefined : this.activeChildForSubsessionLink(link);
|
|
675
|
+
return active !== undefined && this.hasActiveWork(active.runtime.session);
|
|
676
|
+
});
|
|
677
|
+
}
|
|
566
678
|
/**
|
|
567
679
|
* Drive parent notifications from a tracked child's status. Arms a pending
|
|
568
680
|
* notification while the child is working, and when it stops fires a single
|
|
@@ -583,8 +695,12 @@ export class PiSessionService {
|
|
|
583
695
|
this.subsessionNotifyArmed.set(childId, false);
|
|
584
696
|
const status = this.activities.get(childId)?.phase === "error" ? "error" : "idle";
|
|
585
697
|
const finalText = finalAssistantText(historyMessages(session));
|
|
586
|
-
const
|
|
587
|
-
const
|
|
698
|
+
const outputSection = formatSubsessionNotificationOutput(childId, finalText);
|
|
699
|
+
const workingIds = this.workingSubsessionIds(link.parentSessionId);
|
|
700
|
+
const next = workingIds.length === 0
|
|
701
|
+
? "No other tracked subsessions are working."
|
|
702
|
+
: `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.`;
|
|
703
|
+
const text = `Subsession ${childId} stopped working (${status}).\n${next}\n\n${outputSection}`;
|
|
588
704
|
void this.notifyParentOfSubsession(link.parentSessionId, childId, text);
|
|
589
705
|
}
|
|
590
706
|
async getOrOpenParentForSubsession(parentSessionId, childSessionId) {
|
|
@@ -627,23 +743,42 @@ export class PiSessionService {
|
|
|
627
743
|
async status(ref) {
|
|
628
744
|
return this.statusFromSession(await this.getOrOpen(ref));
|
|
629
745
|
}
|
|
746
|
+
/**
|
|
747
|
+
* Join-time snapshot of the in-flight assistant stream. The `seq` watermark and
|
|
748
|
+
* the partial are read together in one synchronous tick (no await between the
|
|
749
|
+
* `currentSeq` read and the `state.streamingMessage` read) so a joining client
|
|
750
|
+
* can seed the partial and then apply only buffered live events with
|
|
751
|
+
* `seq > snapshot.seq`. The partial is browser-projected to strip thinking
|
|
752
|
+
* signatures; it is `null` when no assistant message is mid-stream.
|
|
753
|
+
*/
|
|
754
|
+
async streamSnapshot(ref) {
|
|
755
|
+
const session = await this.getOrOpen(ref);
|
|
756
|
+
// Single consistent tick: capture the watermark and the partial together so
|
|
757
|
+
// the seq matches the partial the client seeds against.
|
|
758
|
+
const seq = this.events.currentSeq(session.sessionId);
|
|
759
|
+
const streamingMessage = session.state.streamingMessage;
|
|
760
|
+
const partial = streamingMessage === undefined || streamingMessage === null
|
|
761
|
+
? null
|
|
762
|
+
: projectBrowserMessage(streamingMessage);
|
|
763
|
+
return { seq, partial };
|
|
764
|
+
}
|
|
630
765
|
async availableModels(ref) {
|
|
631
766
|
const session = await this.getOrOpen(ref);
|
|
632
|
-
session.
|
|
767
|
+
await session.modelRuntime.reloadConfig();
|
|
633
768
|
const models = session.scopedModels.length > 0
|
|
634
769
|
? session.scopedModels.map((scoped) => scoped.model)
|
|
635
|
-
: session.
|
|
770
|
+
: session.modelRuntime.getAvailableSnapshot();
|
|
636
771
|
return models.map(modelToClientModel);
|
|
637
772
|
}
|
|
638
773
|
async setModel(ref, provider, modelId) {
|
|
639
774
|
await this.assertWritable(ref);
|
|
640
775
|
const session = await this.getOrOpen(ref);
|
|
641
|
-
session.
|
|
776
|
+
await session.modelRuntime.reloadConfig();
|
|
642
777
|
const candidates = session.scopedModels.length > 0
|
|
643
778
|
? session.scopedModels.map((scoped) => scoped.model)
|
|
644
|
-
: session.
|
|
779
|
+
: session.modelRuntime.getAvailableSnapshot();
|
|
645
780
|
const model = candidates.find((candidate) => candidate.provider === provider && candidate.id === modelId)
|
|
646
|
-
?? session.
|
|
781
|
+
?? session.modelRuntime.getModel(provider, modelId);
|
|
647
782
|
if (model === undefined)
|
|
648
783
|
throw new Error(`Model not found: ${provider}/${modelId}`);
|
|
649
784
|
await session.setModel(model);
|
|
@@ -996,6 +1131,20 @@ export class PiSessionService {
|
|
|
996
1131
|
clearParentSessionHeader(session.sessionManager);
|
|
997
1132
|
this.unregisterSubsession(session.sessionId);
|
|
998
1133
|
}
|
|
1134
|
+
async clearQueue(ref) {
|
|
1135
|
+
await this.assertWritable(ref);
|
|
1136
|
+
const session = await this.getOrOpen(ref);
|
|
1137
|
+
this.clearCompactionPromptQueue(session.sessionId);
|
|
1138
|
+
clearSessionQueue(session);
|
|
1139
|
+
this.publishStatus(session);
|
|
1140
|
+
return this.statusFromSession(session);
|
|
1141
|
+
}
|
|
1142
|
+
async dismissWarning(ref, dismissId) {
|
|
1143
|
+
const session = await this.getOrOpen(ref);
|
|
1144
|
+
dismissSessionWarning(session, dismissId);
|
|
1145
|
+
this.publishStatus(session);
|
|
1146
|
+
return this.statusFromSession(session);
|
|
1147
|
+
}
|
|
999
1148
|
async abort(ref) {
|
|
1000
1149
|
const active = this.activeForLookup(ref);
|
|
1001
1150
|
if (active === undefined)
|
|
@@ -1343,7 +1492,28 @@ export class PiSessionService {
|
|
|
1343
1492
|
}
|
|
1344
1493
|
}
|
|
1345
1494
|
async bindSessionExtensions(session) {
|
|
1495
|
+
const baseUiContext = session.extensionRunner.getUIContext();
|
|
1496
|
+
const notify = (message, type) => {
|
|
1497
|
+
this.events.publish(session.sessionId, {
|
|
1498
|
+
type: "command.output",
|
|
1499
|
+
level: type === "error" ? "error" : "info",
|
|
1500
|
+
message,
|
|
1501
|
+
});
|
|
1502
|
+
};
|
|
1503
|
+
// PI WEB is a remote UI host, but currently only extension notifications
|
|
1504
|
+
// cross this boundary. Delegate every other UI method to Pi's headless
|
|
1505
|
+
// defaults so unsupported dialogs cancel safely instead of hanging.
|
|
1506
|
+
const uiContext = new Proxy(baseUiContext, {
|
|
1507
|
+
get(target, property, receiver) {
|
|
1508
|
+
if (property === "notify")
|
|
1509
|
+
return notify;
|
|
1510
|
+
const value = Reflect.get(target, property, receiver);
|
|
1511
|
+
return value;
|
|
1512
|
+
},
|
|
1513
|
+
});
|
|
1346
1514
|
await session.bindExtensions({
|
|
1515
|
+
uiContext,
|
|
1516
|
+
mode: "rpc",
|
|
1347
1517
|
onError: (error) => {
|
|
1348
1518
|
const message = `${error.extensionPath}: ${error.error}`;
|
|
1349
1519
|
this.publishActivity(session, "extension error", "error", message);
|
|
@@ -1457,10 +1627,11 @@ export class PiSessionService {
|
|
|
1457
1627
|
this.publishSessionName(session);
|
|
1458
1628
|
}
|
|
1459
1629
|
applyAuthChange(change = {}) {
|
|
1460
|
-
|
|
1630
|
+
// ModelRuntime.login()/logout() refresh the shared runtime before AuthService
|
|
1631
|
+
// emits the change, so no refresh is needed here. Keeping this synchronous
|
|
1632
|
+
// also lets every active session observe the same committed auth snapshot.
|
|
1461
1633
|
for (const active of this.active.values()) {
|
|
1462
1634
|
const { session } = active.runtime;
|
|
1463
|
-
session.modelRegistry.refresh();
|
|
1464
1635
|
this.syncCurrentModelAuthWarning(session, change.removedProviderId);
|
|
1465
1636
|
this.publishStatus(session);
|
|
1466
1637
|
}
|
|
@@ -1472,10 +1643,10 @@ export class PiSessionService {
|
|
|
1472
1643
|
if (model.provider === "unknown" && model.id === "unknown")
|
|
1473
1644
|
return;
|
|
1474
1645
|
const warningKey = authLossWarningKey(session.sessionId, model.provider, model.id);
|
|
1475
|
-
const registered = session.
|
|
1646
|
+
const registered = session.modelRuntime.getModel(model.provider, model.id);
|
|
1476
1647
|
if (registered === undefined)
|
|
1477
1648
|
return;
|
|
1478
|
-
if (session.
|
|
1649
|
+
if (session.modelRuntime.hasConfiguredAuth(model.provider)) {
|
|
1479
1650
|
this.authLossWarnings.delete(warningKey);
|
|
1480
1651
|
return;
|
|
1481
1652
|
}
|
|
@@ -1619,6 +1790,7 @@ export class PiSessionService {
|
|
|
1619
1790
|
const stats = session.getSessionStats();
|
|
1620
1791
|
const model = session.model === undefined ? undefined : modelToClientModel(session.model);
|
|
1621
1792
|
const contextUsage = session.getContextUsage();
|
|
1793
|
+
const warnings = this.warningsForSession(session);
|
|
1622
1794
|
return {
|
|
1623
1795
|
sessionId: session.sessionId,
|
|
1624
1796
|
persisted: sessionFileExists(session.sessionFile),
|
|
@@ -1633,8 +1805,23 @@ export class PiSessionService {
|
|
|
1633
1805
|
tokens: stats.tokens,
|
|
1634
1806
|
cost: stats.cost,
|
|
1635
1807
|
...(contextUsage === undefined ? {} : { contextUsage }),
|
|
1808
|
+
...(warnings.length === 0 ? {} : { warnings }),
|
|
1636
1809
|
};
|
|
1637
1810
|
}
|
|
1811
|
+
/**
|
|
1812
|
+
* Compute the live warning set for a session: runtime/resource diagnostics from
|
|
1813
|
+
* the active runtime (if any) plus the Anthropic subscription-auth notice. Read
|
|
1814
|
+
* fresh on each status publish so a rebuilt runtime or an auth/model change is
|
|
1815
|
+
* reflected without caching a stale snapshot.
|
|
1816
|
+
*/
|
|
1817
|
+
warningsForSession(session) {
|
|
1818
|
+
const runtime = this.active.get(session.sessionId)?.runtime;
|
|
1819
|
+
const warnings = runtime === undefined ? [] : collectRuntimeWarnings(runtime);
|
|
1820
|
+
const anthropic = anthropicSubscriptionWarning(session, join(this.agentDir, "auth.json"));
|
|
1821
|
+
if (anthropic !== undefined)
|
|
1822
|
+
warnings.push(anthropic);
|
|
1823
|
+
return warnings;
|
|
1824
|
+
}
|
|
1638
1825
|
pendingMessageCount(session) {
|
|
1639
1826
|
return session.pendingMessageCount + this.compactionQueuedMessages(session.sessionId).length;
|
|
1640
1827
|
}
|
|
@@ -2096,11 +2283,13 @@ const SUBSESSION_LINK_CUSTOM_TYPE = "pi-web.subsession.link";
|
|
|
2096
2283
|
const SUBSESSION_CHILD_LINK_CUSTOM_TYPE = "pi-web.subsession.spawned";
|
|
2097
2284
|
/** customType marking a parent-facing subsession-completion notice. */
|
|
2098
2285
|
const SUBSESSION_NOTIFICATION_CUSTOM_TYPE = "subsession.completion";
|
|
2099
|
-
const
|
|
2100
|
-
|
|
2101
|
-
|
|
2102
|
-
|
|
2103
|
-
|
|
2286
|
+
const SUBSESSION_NOTIFICATION_MAX_OUTPUT_CHARS = 2000;
|
|
2287
|
+
/** Avoid duplicating a partial result in context when deliberate inspection can return the full output. */
|
|
2288
|
+
function formatSubsessionNotificationOutput(childSessionId, text) {
|
|
2289
|
+
if (text.length > SUBSESSION_NOTIFICATION_MAX_OUTPUT_CHARS) {
|
|
2290
|
+
return `Output from subsession ${childSessionId} was too long for this completion notice and was omitted. Call check_subsession with sessionId "${childSessionId}" to retrieve the final output.`;
|
|
2291
|
+
}
|
|
2292
|
+
return `--- SUBSESSION OUTPUT: ${childSessionId} ---\n${text === "" ? "(no output)" : text}`;
|
|
2104
2293
|
}
|
|
2105
2294
|
/** Most recent assistant text from a history message list, or "" if none. */
|
|
2106
2295
|
function finalAssistantText(messages) {
|