@chainingintention/pi-web-cn 1.202610.3 → 1.202610.4
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 +1 -0
- package/dist/cli.js +129 -122
- package/dist/cli.js.map +1 -1
- package/dist/client/assets/PiWebApp-CN3g7SqA.js +4362 -0
- package/dist/client/assets/external-DUGCSDhZ.js +2 -0
- package/dist/client/assets/index-JduZlbIF.js +2 -0
- package/dist/client/index.html +4 -4
- package/dist/client/manifest.webmanifest +2 -1
- package/dist/localization.js +24 -0
- package/dist/localization.js.map +1 -0
- package/dist/nativeServices/serviceDoctor.js +45 -44
- package/dist/nativeServices/serviceDoctor.js.map +1 -1
- package/dist/pi-packages/captains-log/dist/browser/index.js +46 -45
- package/dist/pi-packages/captains-log/dist/browser/markdown.js +2 -1
- package/dist/pi-packages/captains-log/dist/browser/panel.js +56 -55
- package/dist/pi-packages/captains-log/i18n.js +7 -0
- package/dist/pi-packages/relays/i18n.js +7 -0
- package/dist/pi-packages/relays/markdownDocument.js +2 -1
- package/dist/pi-packages/relays/pi-web-plugin.js +8 -6
- package/dist/pi-packages/relays/relaysPanelElement.js +24 -23
- package/dist/pi-web-plugins/files/browser/pi-web-plugin.js +124 -124
- package/dist/pi-web-plugins/git/browser/git-contract.js +13 -12
- package/dist/pi-web-plugins/git/browser/git-panel.js +25 -24
- package/dist/pi-web-plugins/git/browser/i18n.js +7 -0
- package/dist/pi-web-plugins/git/browser/pi-web-plugin.js +7 -3
- package/dist/pi-web-plugins/git/git-backend.js +10 -9
- package/dist/pi-web-plugins/git/i18n.js +7 -0
- package/dist/pi-web-plugins/git/server-plugin.js +21 -19
- package/dist/pi-web-plugins/info/i18n.js +7 -0
- package/dist/pi-web-plugins/info/infoInternals.js +57 -62
- package/dist/pi-web-plugins/info/pi-web-plugin.js +34 -36
- package/dist/pi-web-plugins/mermaid/browser/pi-web-plugin.js +3 -3
- package/dist/pi-web-plugins/terminal/browser/pi-web-plugin.js +55 -52
- package/dist/pi-web-plugins/terminal/i18n.js +7 -0
- package/dist/pi-web-plugins/terminal/server-plugin.js +23 -21
- package/dist/pi-web-plugins/terminal/terminalService.js +22 -21
- package/dist/pi-web-plugins/updates/i18n.js +7 -0
- package/dist/pi-web-plugins/updates/pi-web-plugin.js +58 -54
- package/dist/pi-web-plugins/updates/updatesLogic.js +21 -20
- package/dist/pi-web-plugins/workspace-tasks/config.js +12 -11
- package/dist/pi-web-plugins/workspace-tasks/i18n.js +7 -0
- package/dist/pi-web-plugins/workspace-tasks/pi-web-plugin.js +8 -6
- package/dist/pi-web-plugins/workspace-tasks/tasksPanelElement.js +20 -19
- package/dist/pi-web-plugins/workspace-tasks/workspaceTasksClient.js +12 -11
- package/dist/pi-web-plugins/zh-cn/auth.json +9 -0
- package/dist/pi-web-plugins/zh-cn/bundled.json +176 -0
- package/dist/pi-web-plugins/zh-cn/cli.json +171 -0
- package/dist/pi-web-plugins/zh-cn/client.json +219 -0
- package/dist/pi-web-plugins/zh-cn/components.json +1002 -0
- package/dist/pi-web-plugins/zh-cn/config-errors.json +172 -0
- package/dist/pi-web-plugins/zh-cn/files.json +41 -0
- package/dist/pi-web-plugins/zh-cn/machines.json +28 -0
- package/dist/pi-web-plugins/zh-cn/main.json +459 -0
- package/dist/pi-web-plugins/zh-cn/management-errors.json +23 -0
- package/dist/pi-web-plugins/zh-cn/manifest.webmanifest +25 -0
- package/dist/pi-web-plugins/zh-cn/metadata.json +3 -0
- package/dist/pi-web-plugins/zh-cn/package.json +10 -0
- package/dist/pi-web-plugins/zh-cn/pi-web-plugin.js +16 -0
- package/dist/pi-web-plugins/zh-cn/projects.json +6 -0
- package/dist/pi-web-plugins/zh-cn/provider-auth.json +17 -0
- package/dist/pi-web-plugins/zh-cn/routing.json +68 -0
- package/dist/pi-web-plugins/zh-cn/server-main.json +99 -0
- package/dist/pi-web-plugins/zh-cn/server.json +174 -0
- package/dist/piWebVersionReport.js +17 -16
- package/dist/piWebVersionReport.js.map +1 -1
- package/dist/plugin-api.d.ts +4 -1
- package/dist/pluginRecoveryCli.js +25 -24
- package/dist/pluginRecoveryCli.js.map +1 -1
- package/dist/server/app.js +43 -28
- package/dist/server/app.js.map +1 -1
- package/dist/server/audit/normalToolAuditCli.js +45 -43
- package/dist/server/audit/normalToolAuditCli.js.map +1 -1
- package/dist/server/configRoutes.js +39 -30
- package/dist/server/configRoutes.js.map +1 -1
- package/dist/server/diagnostics/nodePtyNativeModule.js +7 -6
- package/dist/server/diagnostics/nodePtyNativeModule.js.map +1 -1
- package/dist/server/diagnostics/nodePtySpawnHelper.js +21 -20
- package/dist/server/diagnostics/nodePtySpawnHelper.js.map +1 -1
- package/dist/server/knownAutoInstallPiPackages.js +2 -2
- package/dist/server/knownAutoInstallPiPackages.js.map +1 -1
- package/dist/server/knownPiPackages.js +2 -2
- package/dist/server/knownPiPackages.js.map +1 -1
- package/dist/server/machines/machineClient.js +8 -6
- package/dist/server/machines/machineClient.js.map +1 -1
- package/dist/server/machines/machinePluginProxyRoutes.js +11 -10
- package/dist/server/machines/machinePluginProxyRoutes.js.map +1 -1
- package/dist/server/machines/machineProxyRoutes.js +27 -29
- package/dist/server/machines/machineProxyRoutes.js.map +1 -1
- package/dist/server/machines/machineRoutes.js +10 -12
- package/dist/server/machines/machineRoutes.js.map +1 -1
- package/dist/server/machines/machineService.js +17 -18
- package/dist/server/machines/machineService.js.map +1 -1
- package/dist/server/managementEmbed.js +26 -25
- package/dist/server/managementEmbed.js.map +1 -1
- package/dist/server/normalAuth.js +29 -26
- package/dist/server/normalAuth.js.map +1 -1
- package/dist/server/passwordVerifier.js +2 -1
- package/dist/server/passwordVerifier.js.map +1 -1
- package/dist/server/piPackageService.js +7 -5
- package/dist/server/piPackageService.js.map +1 -1
- package/dist/server/piWebPluginService.js +27 -11
- package/dist/server/piWebPluginService.js.map +1 -1
- package/dist/server/piWebStatus.js +16 -14
- package/dist/server/piWebStatus.js.map +1 -1
- package/dist/server/plugins/serverPluginRuntime.js +4 -2
- package/dist/server/plugins/serverPluginRuntime.js.map +1 -1
- package/dist/server/projects/projectService.js +4 -3
- package/dist/server/projects/projectService.js.map +1 -1
- package/dist/server/sessiond/projectMutationRoutes.js +5 -4
- package/dist/server/sessiond/projectMutationRoutes.js.map +1 -1
- package/dist/server/sessiond/sessionProxyRoutes.js +11 -8
- package/dist/server/sessiond/sessionProxyRoutes.js.map +1 -1
- package/dist/server/sessiond/workspaceCatalogRoutes.js +5 -3
- package/dist/server/sessiond/workspaceCatalogRoutes.js.map +1 -1
- package/dist/server/sessiond.js +8 -3
- package/dist/server/sessiond.js.map +1 -1
- package/dist/server/sessions/authRoutes.js +20 -19
- package/dist/server/sessions/authRoutes.js.map +1 -1
- package/dist/server/sessions/authService.js +13 -10
- package/dist/server/sessions/authService.js.map +1 -1
- package/dist/server/sessions/builtinCommands.js +28 -21
- package/dist/server/sessions/builtinCommands.js.map +1 -1
- package/dist/server/sessions/oauthLoginFlowService.js +17 -15
- package/dist/server/sessions/oauthLoginFlowService.js.map +1 -1
- package/dist/server/sessions/piSessionService.js +229 -208
- package/dist/server/sessions/piSessionService.js.map +1 -1
- package/dist/server/sessions/projectAuthService.js +5 -3
- package/dist/server/sessions/projectAuthService.js.map +1 -1
- package/dist/server/sessions/sessionArchiveMapping.js +8 -7
- package/dist/server/sessions/sessionArchiveMapping.js.map +1 -1
- package/dist/server/sessions/sessionCleanup.js +4 -3
- package/dist/server/sessions/sessionCleanup.js.map +1 -1
- package/dist/server/sessions/sessionCommandService.js +70 -67
- package/dist/server/sessions/sessionCommandService.js.map +1 -1
- package/dist/server/sessions/sessionNotificationStore.js +12 -11
- package/dist/server/sessions/sessionNotificationStore.js.map +1 -1
- package/dist/server/sessions/sessionRoutes.js +74 -72
- package/dist/server/sessions/sessionRoutes.js.map +1 -1
- package/dist/server/sessions/sessionTreeProjection.js +50 -49
- package/dist/server/sessions/sessionTreeProjection.js.map +1 -1
- package/dist/server/sessions/sessionUnreadStore.js +25 -2
- package/dist/server/sessions/sessionUnreadStore.js.map +1 -1
- package/dist/server/terminals/managementCommandRunService.js +13 -12
- package/dist/server/terminals/managementCommandRunService.js.map +1 -1
- package/dist/server/workbench/accessStateStore.js +9 -7
- package/dist/server/workbench/accessStateStore.js.map +1 -1
- package/dist/server/workbench/capabilitySearch.js +3 -2
- package/dist/server/workbench/capabilitySearch.js.map +1 -1
- package/dist/server/workbench/knowledgeSearch.js +8 -7
- package/dist/server/workbench/knowledgeSearch.js.map +1 -1
- package/dist/server/workbench/mcpClient.js +6 -5
- package/dist/server/workbench/mcpClient.js.map +1 -1
- package/dist/server/workbench/skillSync.js +4 -2
- package/dist/server/workbench/skillSync.js.map +1 -1
- package/dist/server/workbench/workbenchClient.js +8 -7
- package/dist/server/workbench/workbenchClient.js.map +1 -1
- package/dist/server/workbench/workbenchTools.js +8 -6
- package/dist/server/workbench/workbenchTools.js.map +1 -1
- package/dist/server/workingDirectory.js +3 -2
- package/dist/server/workingDirectory.js.map +1 -1
- package/dist/server/workspaceExplorerRoutes.js +16 -13
- package/dist/server/workspaceExplorerRoutes.js.map +1 -1
- package/dist/server/workspaces/fileContentService.js +11 -10
- package/dist/server/workspaces/fileContentService.js.map +1 -1
- package/dist/server/workspaces/fileOperationService.js +7 -6
- package/dist/server/workspaces/fileOperationService.js.map +1 -1
- package/dist/server/workspaces/filePreviewService.js +5 -4
- package/dist/server/workspaces/filePreviewService.js.map +1 -1
- package/dist/server/workspaces/fileSuggestions.js +2 -1
- package/dist/server/workspaces/fileSuggestions.js.map +1 -1
- package/dist/server/workspaces/pathAccessPolicy.js +24 -18
- package/dist/server/workspaces/pathAccessPolicy.js.map +1 -1
- package/dist/server/workspaces/pathSafety.js +6 -5
- package/dist/server/workspaces/pathSafety.js.map +1 -1
- package/dist/server/workspaces/projectPiWebConfig.js +3 -2
- package/dist/server/workspaces/projectPiWebConfig.js.map +1 -1
- package/dist/server/workspaces/workspaceRemovalService.js +4 -3
- package/dist/server/workspaces/workspaceRemovalService.js.map +1 -1
- package/dist/server/workspaces/workspaceRouteErrors.js +4 -2
- package/dist/server/workspaces/workspaceRouteErrors.js.map +1 -1
- package/dist/server/workspaces/workspaceUserError.js +18 -0
- package/dist/server/workspaces/workspaceUserError.js.map +1 -0
- package/dist/server-plugin-api.d.ts +3 -1
- package/dist/server-plugin-api.js.map +1 -1
- package/dist/serverPluginRecovery.js +3 -2
- package/dist/serverPluginRecovery.js.map +1 -1
- package/dist/shared/apiTypes.js.map +1 -1
- package/dist/shared/i18n.js +41 -0
- package/dist/shared/i18n.js.map +1 -0
- package/dist/shared/piWebConfigParsing.js +52 -49
- package/dist/shared/piWebConfigParsing.js.map +1 -1
- package/dist/shared/pluginApiTypes.d.ts +7 -0
- package/dist/shared/requiredTerminalPlugin.js +1 -1
- package/dist/shared/requiredTerminalPlugin.js.map +1 -1
- package/dist/shared/sessionDefaults.js +7 -6
- package/dist/shared/sessionDefaults.js.map +1 -1
- package/dist/shared/thinkingLevels.js +17 -16
- package/dist/shared/thinkingLevels.js.map +1 -1
- package/docs/config.md +10 -1
- package/docs/plugins.md +8 -0
- package/package.json +1 -1
- package/dist/client/assets/index-CFhaGb5m.js +0 -4357
|
@@ -1,12 +1,14 @@
|
|
|
1
|
+
import { UserFacingError, createTranslator, userFacingErrorMessage } from "../../shared/i18n.js";
|
|
1
2
|
import { realpathSync, statSync } from "node:fs";
|
|
2
3
|
import { clientSessionFirstMessagePreview } from "./clientSessionPreview.js";
|
|
3
4
|
import { readFile } from "node:fs/promises";
|
|
4
5
|
import { basename, dirname, isAbsolute, join, relative, resolve } from "node:path";
|
|
5
|
-
import { createAgentSessionFromServices, createAgentSessionRuntime, createAgentSessionServices, createEventBus, createEditToolDefinition, createFindToolDefinition, createGrepToolDefinition, getAgentDir, createLsToolDefinition, createReadToolDefinition, createWriteToolDefinition, defineTool, ProjectTrustStore, hasTrustRequiringProjectResources, readStoredCredential, SessionManager, } from "@earendil-works/pi-coding-agent";
|
|
6
|
+
import { createAgentSessionFromServices, createAgentSessionRuntime, createAgentSessionServices, createEventBus, createEditToolDefinition, createFindToolDefinition, createGrepToolDefinition, getAgentDir, createLsToolDefinition, createReadToolDefinition, createWriteToolDefinition, defineTool, loadSkills, ProjectTrustStore, hasTrustRequiringProjectResources, readStoredCredential, SessionManager, } from "@earendil-works/pi-coding-agent";
|
|
6
7
|
import { projectBrowserMessage } from "../browserMessageProjection.js";
|
|
7
8
|
import { pageMessagesAtSafeBoundary } from "./messagePaging.js";
|
|
8
9
|
import { eventScopeFromManagementContext, managementContextKey, NORMAL_SESSION_EVENT_SCOPE } from "../realtime/sessionEventScope.js";
|
|
9
|
-
import {
|
|
10
|
+
import { builtinCommands } from "./builtinCommands.js";
|
|
11
|
+
import { loadNodeTranslator } from "../../localization.js";
|
|
10
12
|
import { SessionCommandService } from "./sessionCommandService.js";
|
|
11
13
|
import { SessionActivityMarker } from "./sessionActivityMarker.js";
|
|
12
14
|
import { projectSessionTree } from "./sessionTreeProjection.js";
|
|
@@ -58,15 +60,12 @@ const DEFAULT_UNREAD_PUBLICATION_RETRY_MS = 1_000;
|
|
|
58
60
|
* is inside: it awaits exactly one call for each, so the phase is a fact rather
|
|
59
61
|
* than a guess. Deliberately free of internal symbol names and file paths.
|
|
60
62
|
*/
|
|
61
|
-
const STARTUP_PHASE_RUNTIME = "正在启动 Pi 会话";
|
|
62
|
-
const STARTUP_PHASE_EXTENSIONS = "正在加载会话扩展";
|
|
63
63
|
/**
|
|
64
64
|
* Appended to whichever phase is running when a background provider catalog
|
|
65
65
|
* refresh happens to be in flight. It is stated as a concurrent fact, never as
|
|
66
66
|
* the cause: PI WEB can verify that a refresh is running, but not that this
|
|
67
67
|
* particular startup is waiting on it.
|
|
68
68
|
*/
|
|
69
|
-
const STARTUP_CONCURRENT_CATALOG_REFRESH = "服务商模型列表正在刷新";
|
|
70
69
|
const MAX_UNREAD_PUBLICATION_RETRY_MS = 30_000;
|
|
71
70
|
const MAX_PENDING_UNREAD_MUTATIONS = SESSION_UNREAD_LIMIT + 1;
|
|
72
71
|
const IDLE_SESSION_FILE_RESOLUTION_THROTTLE_MS = 30_000;
|
|
@@ -84,13 +83,13 @@ function auditVisibleAssistantMessage(message) {
|
|
|
84
83
|
function noop() {
|
|
85
84
|
// Intentionally empty default unsubscribe callback.
|
|
86
85
|
}
|
|
87
|
-
function spawnTargetError(decision) {
|
|
86
|
+
function spawnTargetError(decision, t) {
|
|
88
87
|
if (decision.reason === "not-registered")
|
|
89
|
-
return new Error("
|
|
90
|
-
return new Error(
|
|
88
|
+
return new Error(t("session.spawnUnregistered", "Spawned session is outside registered projects"));
|
|
89
|
+
return new Error(t("session.spawnWorkspace", "cwd must be a workspace of this project. Allowed paths: {paths}", { paths: decision.allowedCwds.join(", ") }));
|
|
91
90
|
}
|
|
92
|
-
function subsessionCwdError(spawningCwd, requestedCwd) {
|
|
93
|
-
return new Error(
|
|
91
|
+
function subsessionCwdError(spawningCwd, requestedCwd, t = loadNodeTranslator("server")) {
|
|
92
|
+
return new Error(t("server.main.3122d89b6904", "A tracked subsession runs in this session's working directory ({arg0}); {arg1} was requested. Instruct the child to work elsewhere from this workspace, or use spawn_session for an independent session in another workspace.", { arg0: spawningCwd, arg1: requestedCwd }));
|
|
94
93
|
}
|
|
95
94
|
function modelSpecOf(model) {
|
|
96
95
|
return `${model.provider}/${model.id}`;
|
|
@@ -111,8 +110,8 @@ function parseModelSpec(spec) {
|
|
|
111
110
|
* (a list would invite guesses). The agent loop turns the throw into an
|
|
112
111
|
* error tool result; how to recover is the agent's call.
|
|
113
112
|
*/
|
|
114
|
-
function unknownSpawnModelError(modelSpec) {
|
|
115
|
-
return new Error(
|
|
113
|
+
function unknownSpawnModelError(modelSpec, t) {
|
|
114
|
+
return new Error(t("model.spawnUnknown", 'Model "{model}" not found. Supply an exact "provider/model-id".', { model: modelSpec }));
|
|
116
115
|
}
|
|
117
116
|
function authLossWarningKey(sessionId, eventScope, provider, modelId) {
|
|
118
117
|
return `${eventScope}:${sessionId}:${provider}/${modelId}`;
|
|
@@ -132,7 +131,7 @@ function lookupMatchesStartupSession(ref, session) {
|
|
|
132
131
|
function activeSessionKey(sessionId, eventScope) {
|
|
133
132
|
return `${eventScope}\0${sessionId}`;
|
|
134
133
|
}
|
|
135
|
-
function sessionTreeNavigationOptions(request) {
|
|
134
|
+
function sessionTreeNavigationOptions(request, t = loadNodeTranslator("server")) {
|
|
136
135
|
switch (request.summary.mode) {
|
|
137
136
|
case "none":
|
|
138
137
|
return { summarize: false };
|
|
@@ -141,9 +140,9 @@ function sessionTreeNavigationOptions(request) {
|
|
|
141
140
|
case "custom": {
|
|
142
141
|
const customInstructions = request.summary.instructions.trim();
|
|
143
142
|
if (customInstructions === "")
|
|
144
|
-
throw new Error("
|
|
143
|
+
throw new Error(t("server.main.416db415bedb", "Custom branch-summary instructions are required"));
|
|
145
144
|
if (customInstructions.length > SESSION_TREE_CUSTOM_INSTRUCTIONS_MAX_LENGTH) {
|
|
146
|
-
throw new Error(
|
|
145
|
+
throw new Error(t("server.main.8e5676bb8f61", "Custom branch-summary instructions must be at most {arg0} characters", { arg0: String(SESSION_TREE_CUSTOM_INSTRUCTIONS_MAX_LENGTH) }));
|
|
147
146
|
}
|
|
148
147
|
return { summarize: true, customInstructions };
|
|
149
148
|
}
|
|
@@ -163,9 +162,9 @@ function decrementMapCount(counts, key) {
|
|
|
163
162
|
else
|
|
164
163
|
counts.set(key, remaining);
|
|
165
164
|
}
|
|
166
|
-
function requirePromptText(value) {
|
|
165
|
+
function requirePromptText(value, t) {
|
|
167
166
|
if (typeof value !== "string")
|
|
168
|
-
throw new Error("
|
|
167
|
+
throw new Error(t("session.promptRequired", "Prompt text is required"));
|
|
169
168
|
return value;
|
|
170
169
|
}
|
|
171
170
|
function parsePromptStreamingBehavior(value) {
|
|
@@ -173,7 +172,7 @@ function parsePromptStreamingBehavior(value) {
|
|
|
173
172
|
return undefined;
|
|
174
173
|
if (value === "steer" || value === "followUp")
|
|
175
174
|
return value;
|
|
176
|
-
throw new
|
|
175
|
+
throw new UserFacingError("routing.streamingBehaviorInvalid", "Prompt streamingBehavior must be \"steer\" or \"followUp\"");
|
|
177
176
|
}
|
|
178
177
|
const GLOBAL_AGENT_CONTEXT_FILE_NAMES = new Set(["AGENTS.md", "AGENTS.MD", "CLAUDE.md", "CLAUDE.MD"]);
|
|
179
178
|
export function filterPrivilegedManagementContextFiles(cwd, context, base) {
|
|
@@ -199,30 +198,46 @@ export function filterManagedGlobalContextFiles(cwd, agentDir, base) {
|
|
|
199
198
|
}),
|
|
200
199
|
};
|
|
201
200
|
}
|
|
202
|
-
export function filterManagedProjectSkills(cwd, base) {
|
|
201
|
+
export function filterManagedProjectSkills(cwd, base, agentDir = getAgentDir()) {
|
|
203
202
|
const projectRoot = realpathSync(cwd);
|
|
204
203
|
const pathAllowed = (path) => pathInsideRealDirectory(projectRoot, path);
|
|
205
|
-
return
|
|
206
|
-
...base,
|
|
207
|
-
skills: base.skills.filter((skill) => pathAllowed(skill.filePath)),
|
|
208
|
-
diagnostics: filterManagedSkillDiagnostics(base.diagnostics, pathAllowed),
|
|
209
|
-
};
|
|
204
|
+
return filterManagedSkills(cwd, agentDir, base, pathAllowed);
|
|
210
205
|
}
|
|
211
|
-
export function filterManagedWorkbenchSkills(cwd, base, receipt) {
|
|
206
|
+
export function filterManagedWorkbenchSkills(cwd, base, receipt, agentDir = getAgentDir()) {
|
|
212
207
|
const projectRoot = realpathSync(cwd);
|
|
213
208
|
const allowedDirectories = new Set([
|
|
214
209
|
...MANAGED_RELAY_SKILL_NAMES.map((name) => resolve(projectRoot, ".pi", "skills", name)),
|
|
215
210
|
...receipt.skills.map((skill) => resolve(projectRoot, ".pi", "skills", skill.directory)),
|
|
216
211
|
]);
|
|
217
212
|
const pathAllowed = (path) => [...allowedDirectories].some((directory) => pathInsideRealDirectory(directory, path));
|
|
218
|
-
return
|
|
219
|
-
...base,
|
|
220
|
-
skills: base.skills.filter((skill) => pathAllowed(skill.filePath)),
|
|
221
|
-
diagnostics: filterManagedSkillDiagnostics(base.diagnostics, pathAllowed),
|
|
222
|
-
};
|
|
213
|
+
return filterManagedSkills(cwd, agentDir, base, pathAllowed);
|
|
223
214
|
}
|
|
224
|
-
function
|
|
225
|
-
|
|
215
|
+
function filterManagedSkills(cwd, agentDir, base, pathAllowed) {
|
|
216
|
+
const skills = base.skills.filter((skill) => pathAllowed(skill.filePath));
|
|
217
|
+
const shadowedPaths = new Set();
|
|
218
|
+
const diagnostics = base.diagnostics.filter((diagnostic) => {
|
|
219
|
+
const collision = diagnostic.collision;
|
|
220
|
+
if (diagnostic.type === "collision" && collision?.resourceType === "skill") {
|
|
221
|
+
const winnerAllowed = pathAllowed(collision.winnerPath);
|
|
222
|
+
const loserAllowed = pathAllowed(collision.loserPath);
|
|
223
|
+
if (!winnerAllowed && loserAllowed)
|
|
224
|
+
shadowedPaths.add(collision.loserPath);
|
|
225
|
+
return winnerAllowed && loserAllowed;
|
|
226
|
+
}
|
|
227
|
+
return diagnostic.path === undefined || pathAllowed(diagnostic.path);
|
|
228
|
+
});
|
|
229
|
+
// The SDK deduplicates names before this override. Restore permitted losers
|
|
230
|
+
// instead of letting a discarded global/package winner hide them entirely.
|
|
231
|
+
if (shadowedPaths.size > 0) {
|
|
232
|
+
const recovered = loadSkills({ cwd, agentDir, skillPaths: [...shadowedPaths], includeDefaults: false });
|
|
233
|
+
skills.push(...recovered.skills.filter((skill) => pathAllowed(skill.filePath)));
|
|
234
|
+
for (const diagnostic of recovered.diagnostics) {
|
|
235
|
+
if (!diagnostics.some((existing) => existing.type === diagnostic.type && existing.path === diagnostic.path && existing.message === diagnostic.message)) {
|
|
236
|
+
diagnostics.push(diagnostic);
|
|
237
|
+
}
|
|
238
|
+
}
|
|
239
|
+
}
|
|
240
|
+
return { ...base, skills, diagnostics };
|
|
226
241
|
}
|
|
227
242
|
function pathInsideRealDirectory(directory, path) {
|
|
228
243
|
try {
|
|
@@ -337,13 +352,13 @@ export function anthropicSubscriptionWarning(session, authPath) {
|
|
|
337
352
|
*/
|
|
338
353
|
export function dismissSessionWarning(session, dismissId) {
|
|
339
354
|
if (dismissId !== ANTHROPIC_EXTRA_USAGE_DISMISS_ID) {
|
|
340
|
-
throw new
|
|
355
|
+
throw new UserFacingError("routing.warningDismissInvalid", "Unknown session warning dismiss id: {arg0}", { arg0: dismissId });
|
|
341
356
|
}
|
|
342
357
|
session.settingsManager.setWarnings({ ...session.settingsManager.getWarnings(), anthropicExtraUsage: false });
|
|
343
358
|
}
|
|
344
359
|
function defaultCreateAgentRuntime(createRuntime, options) {
|
|
345
360
|
if (!(options.sessionManager instanceof SessionManager))
|
|
346
|
-
throw new
|
|
361
|
+
throw new UserFacingError("routing.runtimeManagerRequired", "Default runtime creation requires an SDK SessionManager");
|
|
347
362
|
const runtimeFactory = createRuntimeWithContextAndOneShotSessionOptions(createRuntime, options.managementContext, options.initialModel, options.initialThinkingLevel, options.delegationToolsEnabled);
|
|
348
363
|
return createAgentSessionRuntime(runtimeFactory, {
|
|
349
364
|
cwd: options.cwd,
|
|
@@ -556,7 +571,7 @@ function createManagementRuntimeFactory(modelRuntime, resolveSettingsScope, sess
|
|
|
556
571
|
settingsManager,
|
|
557
572
|
resourceLoaderOptions: {
|
|
558
573
|
agentsFilesOverride: (base) => filterManagedGlobalContextFiles(cwd, agentDir, base),
|
|
559
|
-
skillsOverride: (base) => receipt === undefined ? filterManagedProjectSkills(cwd, base) : filterManagedWorkbenchSkills(cwd, base, receipt),
|
|
574
|
+
skillsOverride: (base) => receipt === undefined ? filterManagedProjectSkills(cwd, base, agentDir) : filterManagedWorkbenchSkills(cwd, base, receipt, agentDir),
|
|
560
575
|
},
|
|
561
576
|
}), projectTrustDiagnostics: [] };
|
|
562
577
|
const modelOptions = await resolveSessionModelOptions({
|
|
@@ -585,7 +600,11 @@ function createManagementRuntimeFactory(modelRuntime, resolveSettingsScope, sess
|
|
|
585
600
|
const result = await createAgentSessionFromServices(options);
|
|
586
601
|
// SDK reload re-executes extensions, so it needs the same scoped environment as startup.
|
|
587
602
|
const reload = result.session.reload.bind(result.session);
|
|
588
|
-
result.session.reload = (options) => withRuntimeCreationEnvironment(resourceEnvironment, () =>
|
|
603
|
+
result.session.reload = (options) => withRuntimeCreationEnvironment(resourceEnvironment, async () => {
|
|
604
|
+
if (!normalPermissions)
|
|
605
|
+
await ensureManagedRelaySkills(cwd);
|
|
606
|
+
return reload(options);
|
|
607
|
+
});
|
|
589
608
|
return { ...result, services, diagnostics: [...projectTrustDiagnostics, ...services.diagnostics] };
|
|
590
609
|
});
|
|
591
610
|
};
|
|
@@ -686,6 +705,7 @@ export class PiSessionService {
|
|
|
686
705
|
this.pendingUnreadMutations = [];
|
|
687
706
|
this.unreadPublicationFlushRequested = false;
|
|
688
707
|
this.unreadPublicationStopped = false;
|
|
708
|
+
this.t = deps.translator ?? loadNodeTranslator("server");
|
|
689
709
|
this.archiveStore = deps.archiveStore ?? new SessionArchiveStore();
|
|
690
710
|
this.pinStore = deps.pinStore ?? new SessionPinStore(join(deps.dataDir ?? piWebDataDir(), "session-pins.json"));
|
|
691
711
|
this.agentDir = deps.agentDir ?? getAgentDir();
|
|
@@ -741,13 +761,13 @@ export class PiSessionService {
|
|
|
741
761
|
this.heartbeat = setInterval(() => { this.publishHeartbeats(); }, deps.heartbeatIntervalMs ?? 2000);
|
|
742
762
|
this.commandService = new SessionCommandService((sessionId, eventScope) => this.getActiveForCommand(sessionId, eventScope), (sessionId, text, eventScope) => this.submitCommandPrompt(sessionId, text, eventScope), events, {
|
|
743
763
|
onCompactionStart: (session) => {
|
|
744
|
-
this.beginSessionEntryMutation(session, "
|
|
745
|
-
this.publishActivity(session, "
|
|
764
|
+
this.beginSessionEntryMutation(session, this.t("session.action.compact", "compact the session"));
|
|
765
|
+
this.publishActivity(session, this.t("activity.compacting", "Compacting context"), "active");
|
|
746
766
|
this.publishStatus(session);
|
|
747
767
|
},
|
|
748
768
|
onCompactionEnd: (session, result, detail) => {
|
|
749
769
|
this.endSessionEntryMutation(session);
|
|
750
|
-
this.publishActivity(session, result === "success" ? "
|
|
770
|
+
this.publishActivity(session, result === "success" ? this.t("activity.compacted", "Context compacted") : this.t("activity.compactionFailed", "Context compaction failed"), result === "success" ? "idle" : "error", detail);
|
|
751
771
|
this.publishStatus(session);
|
|
752
772
|
},
|
|
753
773
|
reloadSession: (session) => this.reloadSessionRuntime(session),
|
|
@@ -760,9 +780,9 @@ export class PiSessionService {
|
|
|
760
780
|
isTreeNavigationActive: (session) => this.treeNavigations.has(session),
|
|
761
781
|
runSessionReplacement: (session, operation) => {
|
|
762
782
|
const runtime = this.activeRuntimeForSession(session);
|
|
763
|
-
return this.runTreeExclusiveOperation([{ sessionId: session.sessionId, session, ...(runtime === undefined ? {} : { runtime }) }], "
|
|
783
|
+
return this.runTreeExclusiveOperation([{ sessionId: session.sessionId, session, ...(runtime === undefined ? {} : { runtime }) }], this.t("session.stopBeforeReplace", "Stop current session activity before replacing the session"), operation);
|
|
764
784
|
},
|
|
765
|
-
}, { listSessionNames: (cwd) => this.listSessionNames(cwd) });
|
|
785
|
+
}, { listSessionNames: (cwd) => this.listSessionNames(cwd) }, this.t);
|
|
766
786
|
}
|
|
767
787
|
activeCount() {
|
|
768
788
|
return this.active.size;
|
|
@@ -783,7 +803,7 @@ export class PiSessionService {
|
|
|
783
803
|
const scope = eventScopeFromManagementContext(context);
|
|
784
804
|
const plan = await this.cleanupPlan(request);
|
|
785
805
|
if (plan.deleteRecords.length > 0 && this.archiveStore.deleteArchived === undefined && this.archiveStore.deleteArchivedMany === undefined)
|
|
786
|
-
throw new Error("
|
|
806
|
+
throw new Error(this.t("session.archiveDeleteUnsupported", "The archive store does not support deletion"));
|
|
787
807
|
const archiveInputs = [];
|
|
788
808
|
const readyArchiveInputs = [];
|
|
789
809
|
const deleteRecords = [];
|
|
@@ -900,13 +920,13 @@ export class PiSessionService {
|
|
|
900
920
|
}
|
|
901
921
|
async setPinned(ref, pinned, managementContext) {
|
|
902
922
|
if (ref.cwd === "")
|
|
903
|
-
throw new Error("
|
|
923
|
+
throw new Error(this.t("session.pinWorkspaceRequired", "A workspace is required to pin a session"));
|
|
904
924
|
const sessions = await this.list(ref.cwd, managementContext);
|
|
905
925
|
const session = sessions.find((candidate) => candidate.id === ref.id);
|
|
906
926
|
if (session === undefined)
|
|
907
|
-
throw new Error("
|
|
927
|
+
throw new Error(this.t("session.notFound", "Session not found"));
|
|
908
928
|
if (session.archived !== true && session.persisted === false)
|
|
909
|
-
throw new Error("
|
|
929
|
+
throw new Error(this.t("session.pinPersistedOnly", "Only persisted sessions can be pinned"));
|
|
910
930
|
await this.pinStore.set(sessionPinScope(ref.cwd, managementContext), session.id, pinned);
|
|
911
931
|
return { pinned };
|
|
912
932
|
}
|
|
@@ -950,7 +970,7 @@ export class PiSessionService {
|
|
|
950
970
|
}
|
|
951
971
|
/** Atomically start and prompt one visible normal-mode, non-delegating session. */
|
|
952
972
|
async startOneShotRun(cwd, text, signal) {
|
|
953
|
-
const promptText = requirePromptText(text);
|
|
973
|
+
const promptText = requirePromptText(text, this.t);
|
|
954
974
|
return this.startManagedSession(cwd, signal, promptText);
|
|
955
975
|
}
|
|
956
976
|
async startManagedSession(cwd, signal, promptText) {
|
|
@@ -973,7 +993,7 @@ export class PiSessionService {
|
|
|
973
993
|
await this.closeActive(session.sessionId);
|
|
974
994
|
}
|
|
975
995
|
catch (cleanupError) {
|
|
976
|
-
throw new AggregateError([error, cleanupError],
|
|
996
|
+
throw new AggregateError([error, cleanupError], this.t("server.main.dbc19a47e7b3", "Failed to clean up managed session {arg0} after startup failed", { arg0: session.sessionId }), { cause: cleanupError });
|
|
977
997
|
}
|
|
978
998
|
throw error;
|
|
979
999
|
}
|
|
@@ -1014,10 +1034,10 @@ export class PiSessionService {
|
|
|
1014
1034
|
*/
|
|
1015
1035
|
async spawnSession(input) {
|
|
1016
1036
|
if (this.spawnTargets === undefined)
|
|
1017
|
-
throw new Error("
|
|
1037
|
+
throw new Error(this.t("session.spawningDisabled", "Session spawning is disabled"));
|
|
1018
1038
|
const decision = await this.spawnTargets.resolveSpawnTarget(input.spawningCwd, input.cwd, eventScopeFromManagementContext(input.managementContext));
|
|
1019
1039
|
if (!decision.allowed)
|
|
1020
|
-
throw spawnTargetError(decision);
|
|
1040
|
+
throw spawnTargetError(decision, this.t);
|
|
1021
1041
|
// A model spec overrides the inherited model. Only a spec triggers a
|
|
1022
1042
|
// spawning-session lookup; the default path must not depend on it.
|
|
1023
1043
|
let model = input.model;
|
|
@@ -1043,13 +1063,13 @@ export class PiSessionService {
|
|
|
1043
1063
|
/** Start a tracked child in the parent's workspace so it remains visible in the same session tree. */
|
|
1044
1064
|
async spawnSubsession(input) {
|
|
1045
1065
|
if (this.spawnTargets === undefined)
|
|
1046
|
-
throw new Error("
|
|
1066
|
+
throw new Error(this.t("session.spawningDisabled", "Session spawning is disabled"));
|
|
1047
1067
|
if (input.cwd !== undefined && input.cwd !== "" && !cwdPathsEqual(input.cwd, input.spawningCwd)) {
|
|
1048
|
-
throw subsessionCwdError(input.spawningCwd, input.cwd);
|
|
1068
|
+
throw subsessionCwdError(input.spawningCwd, input.cwd, this.t);
|
|
1049
1069
|
}
|
|
1050
1070
|
const decision = await this.spawnTargets.resolveSpawnTarget(input.spawningCwd, undefined, eventScopeFromManagementContext(input.managementContext));
|
|
1051
1071
|
if (!decision.allowed)
|
|
1052
|
-
throw spawnTargetError(decision);
|
|
1072
|
+
throw spawnTargetError(decision, this.t);
|
|
1053
1073
|
// A model spec overrides the inherited model and is resolved against the
|
|
1054
1074
|
// parent's model runtime; only a spec triggers that lookup.
|
|
1055
1075
|
const model = input.modelSpec === undefined
|
|
@@ -1196,7 +1216,7 @@ export class PiSessionService {
|
|
|
1196
1216
|
: candidates.find((candidate) => candidate.provider === parsed.provider && candidate.id === parsed.modelId)
|
|
1197
1217
|
?? session.modelRuntime.getModel(parsed.provider, parsed.modelId);
|
|
1198
1218
|
if (model === undefined)
|
|
1199
|
-
throw unknownSpawnModelError(modelSpec);
|
|
1219
|
+
throw unknownSpawnModelError(modelSpec, this.t);
|
|
1200
1220
|
return model;
|
|
1201
1221
|
}
|
|
1202
1222
|
/**
|
|
@@ -1233,7 +1253,7 @@ export class PiSessionService {
|
|
|
1233
1253
|
const session = await this.getOrOpen(ref, managementContext);
|
|
1234
1254
|
// Checked before the store closes the ask so a refused delivery cannot
|
|
1235
1255
|
// discard answers the user already submitted.
|
|
1236
|
-
this.assertTreeNavigationInactive(session, "
|
|
1256
|
+
this.assertTreeNavigationInactive(session, this.t("session.action.answer", "answer questions"));
|
|
1237
1257
|
return this.closeAsk(session, this.pendingAskStore.submit(this.dialogSessionKey(session), askId, submission));
|
|
1238
1258
|
}
|
|
1239
1259
|
/**
|
|
@@ -1244,7 +1264,7 @@ export class PiSessionService {
|
|
|
1244
1264
|
async cancelAsk(ref, askId, managementContext) {
|
|
1245
1265
|
await this.assertWritable(ref);
|
|
1246
1266
|
const session = await this.getOrOpen(ref, managementContext);
|
|
1247
|
-
this.assertTreeNavigationInactive(session, "
|
|
1267
|
+
this.assertTreeNavigationInactive(session, this.t("session.action.dismiss", "dismiss questions"));
|
|
1248
1268
|
return this.closeAsk(session, this.pendingAskStore.cancel(this.dialogSessionKey(session), askId));
|
|
1249
1269
|
}
|
|
1250
1270
|
/**
|
|
@@ -1258,7 +1278,7 @@ export class PiSessionService {
|
|
|
1258
1278
|
return { result: "stale", sessionStatus: this.statusFromSession(session) };
|
|
1259
1279
|
const { outcome } = result;
|
|
1260
1280
|
this.publishAskClosed(session.sessionId, outcome, this.eventScopeForSession(session));
|
|
1261
|
-
await this.runSessionEntryMutation(session, "
|
|
1281
|
+
await this.runSessionEntryMutation(session, this.t("server.main.07f784061918", "deliver answers to your questions"), () => session.sendCustomMessage({ customType: ASK_USER_ANSWERS_CUSTOM_TYPE, content: renderAskUserAnswersText(outcome), display: true, details: outcome }, { triggerTurn: true, deliverAs: "followUp" }));
|
|
1262
1282
|
this.publishStatus(session);
|
|
1263
1283
|
return { result: "closed", outcome, sessionStatus: this.statusFromSession(session) };
|
|
1264
1284
|
}
|
|
@@ -1276,7 +1296,7 @@ export class PiSessionService {
|
|
|
1276
1296
|
if (outcome === undefined)
|
|
1277
1297
|
return;
|
|
1278
1298
|
this.publishAskClosed(session.sessionId, outcome, this.eventScopeForSession(session));
|
|
1279
|
-
await this.runSessionEntryMutation(session, "
|
|
1299
|
+
await this.runSessionEntryMutation(session, this.t("server.main.90b5a8ac7c7d", "void the open questions"), () => session.sendCustomMessage({ customType: ASK_USER_ANSWERS_CUSTOM_TYPE, content: renderAskUserAnswersText(outcome), display: true, details: outcome }, { triggerTurn: false, deliverAs: "followUp" }));
|
|
1280
1300
|
this.publishStatus(session);
|
|
1281
1301
|
}
|
|
1282
1302
|
/**
|
|
@@ -1509,10 +1529,10 @@ export class PiSessionService {
|
|
|
1509
1529
|
return canonicalizeStoredCwd(this.sessionManager.open(childSessionFile).getCwd());
|
|
1510
1530
|
}
|
|
1511
1531
|
catch (error) {
|
|
1512
|
-
throw new Error("Could not resolve cwd for verified tracked sub-session", { cause: error });
|
|
1532
|
+
throw new Error(this.t("session.subsessionCwdMissing", "Could not resolve cwd for verified tracked sub-session"), { cause: error });
|
|
1513
1533
|
}
|
|
1514
1534
|
}
|
|
1515
|
-
throw new Error("Could not resolve cwd for verified tracked sub-session");
|
|
1535
|
+
throw new Error(this.t("session.subsessionCwdMissing", "Could not resolve cwd for verified tracked sub-session"));
|
|
1516
1536
|
}
|
|
1517
1537
|
unregisterSubsession(childSessionId, scope) {
|
|
1518
1538
|
const childKey = activeSessionKey(childSessionId, scope);
|
|
@@ -1638,13 +1658,13 @@ export class PiSessionService {
|
|
|
1638
1658
|
return active.runtime.session;
|
|
1639
1659
|
if (link.childSessionFile !== undefined) {
|
|
1640
1660
|
if (!(await sessionFileHeaderMatches(link.childSessionFile, { sessionId, parentSessionFile: link.parentSessionFile })))
|
|
1641
|
-
throw new Error("Session not found");
|
|
1661
|
+
throw new Error(this.t("session.notFound", "Session not found"));
|
|
1642
1662
|
const sessionManager = this.sessionManager.open(link.childSessionFile);
|
|
1643
1663
|
return (await this.create(sessionManager, link.cwd ?? sessionManager.getCwd(), {
|
|
1644
1664
|
...(link.managementContext === undefined ? {} : { managementContext: link.managementContext }),
|
|
1645
1665
|
})).runtime.session;
|
|
1646
1666
|
}
|
|
1647
|
-
throw new Error("Session not found");
|
|
1667
|
+
throw new Error(this.t("session.notFound", "Session not found"));
|
|
1648
1668
|
}
|
|
1649
1669
|
async subsessionSummaryFields(childSessionId, scope) {
|
|
1650
1670
|
const link = this.subsessionLinks.get(activeSessionKey(childSessionId, scope));
|
|
@@ -1712,9 +1732,9 @@ export class PiSessionService {
|
|
|
1712
1732
|
return active.runtime.session;
|
|
1713
1733
|
const parentSessionFile = link.parentSessionFile;
|
|
1714
1734
|
if (parentSessionFile === undefined)
|
|
1715
|
-
throw new Error(
|
|
1735
|
+
throw new Error(this.t("session.parentUnavailable", "Parent session {parentSessionId} is not available for subsession notification", { parentSessionId }));
|
|
1716
1736
|
if ((await readSessionHeaderSummary(parentSessionFile))?.id !== parentSessionId) {
|
|
1717
|
-
throw new Error(
|
|
1737
|
+
throw new Error(this.t("session.parentUnavailable", "Parent session {parentSessionId} is not available for subsession notification", { parentSessionId }));
|
|
1718
1738
|
}
|
|
1719
1739
|
const sessionManager = this.sessionManager.open(parentSessionFile);
|
|
1720
1740
|
return (await this.create(sessionManager, sessionManager.getCwd(), {
|
|
@@ -1746,7 +1766,8 @@ export class PiSessionService {
|
|
|
1746
1766
|
}
|
|
1747
1767
|
async messages(ref, page, managementContext) {
|
|
1748
1768
|
const session = await this.getOrOpen(ref, managementContext);
|
|
1749
|
-
|
|
1769
|
+
const branch = await this.readableSessionBranch(session);
|
|
1770
|
+
return { ...pageMessagesAtSafeBoundary(historyMessagesFromEntries(branch), page), leafId: session.sessionManager.getLeafId() };
|
|
1750
1771
|
}
|
|
1751
1772
|
async status(ref, managementContext) {
|
|
1752
1773
|
const session = await this.sessionForStatusOrDialogClose(ref, managementContext);
|
|
@@ -1803,7 +1824,7 @@ export class PiSessionService {
|
|
|
1803
1824
|
const target = `${update.provider}/${update.modelId}`;
|
|
1804
1825
|
const available = session.modelRuntime.getAvailableSnapshot();
|
|
1805
1826
|
if (!available.some((model) => modelScopeId(model) === target))
|
|
1806
|
-
throw new Error(
|
|
1827
|
+
throw new Error(this.t("server.main.583c6c767d31", "Model not found: {arg0}", { arg0: target }));
|
|
1807
1828
|
const enabledIds = await resolveEnabledModelIds({
|
|
1808
1829
|
settingsManager: settings,
|
|
1809
1830
|
// eslint-disable-next-line @typescript-eslint/consistent-type-assertions
|
|
@@ -1811,7 +1832,7 @@ export class PiSessionService {
|
|
|
1811
1832
|
scopedModels: [],
|
|
1812
1833
|
});
|
|
1813
1834
|
if (enabledIds !== null && !enabledIds.includes(target))
|
|
1814
|
-
throw new Error(
|
|
1835
|
+
throw new Error(this.t("server.main.c24071bdc942", "Model is not enabled: {arg0}", { arg0: target }));
|
|
1815
1836
|
settings.setDefaultModelAndProvider(update.provider, update.modelId);
|
|
1816
1837
|
}
|
|
1817
1838
|
else if (update.thinkingLevel !== undefined) {
|
|
@@ -1839,7 +1860,7 @@ export class PiSessionService {
|
|
|
1839
1860
|
assertDefaultsSettingsHealthy(settings) {
|
|
1840
1861
|
const errors = settings.drainErrors();
|
|
1841
1862
|
if (errors.length > 0)
|
|
1842
|
-
throw new Error(
|
|
1863
|
+
throw new Error(this.t("server.main.d04dfe5abc11", "Session defaults settings failed: {arg0}", { arg0: errors.map(({ error }) => error.message).join("; ") }));
|
|
1843
1864
|
}
|
|
1844
1865
|
async availableModels(ref, managementContext) {
|
|
1845
1866
|
const session = await this.getOrOpen(ref, managementContext);
|
|
@@ -1853,25 +1874,25 @@ export class PiSessionService {
|
|
|
1853
1874
|
}
|
|
1854
1875
|
async setModelEnabled(ref, provider, modelId, enabled, managementContext) {
|
|
1855
1876
|
if (managementContext !== undefined)
|
|
1856
|
-
throw new Error("
|
|
1877
|
+
throw new Error(this.t("server.main.2a7cb0e35521", "Enabled model scope cannot be changed in management embed mode"));
|
|
1857
1878
|
await this.assertWritable(ref);
|
|
1858
1879
|
const session = await this.getOrOpen(ref);
|
|
1859
1880
|
return this.runModelScopeMutation(async () => {
|
|
1860
1881
|
const scope = this.modelScopeContext(session);
|
|
1861
1882
|
if (!scope.editable)
|
|
1862
|
-
throw new Error("
|
|
1863
|
-
this.assertTreeNavigationInactive(session, "
|
|
1883
|
+
throw new Error(this.t("server.main.c43b4e3cefe1", "Model availability is controlled by this workspace's .pi/settings.json"));
|
|
1884
|
+
this.assertTreeNavigationInactive(session, this.t("server.main.fd81137332d5", "change enabled models"));
|
|
1864
1885
|
await session.modelRuntime.refresh({ allowNetwork: false });
|
|
1865
1886
|
const currentIds = (await this.modelScopeSnapshot(session, scope)).enabledIds;
|
|
1866
1887
|
const available = session.modelRuntime.getAvailableSnapshot();
|
|
1867
1888
|
const availableIds = available.map(modelScopeId);
|
|
1868
1889
|
const targetId = `${provider}/${modelId}`;
|
|
1869
1890
|
if (!availableIds.includes(targetId))
|
|
1870
|
-
throw new Error(
|
|
1891
|
+
throw new Error(this.t("server.main.583c6c767d31", "Model not found: {arg0}", { arg0: targetId }));
|
|
1871
1892
|
if (!enabled && session.model !== undefined && modelScopeId(session.model) === targetId)
|
|
1872
|
-
throw new Error("
|
|
1893
|
+
throw new Error(this.t("server.main.fe143303e69e", "Current model cannot be disabled"));
|
|
1873
1894
|
const nextIds = applyEnabledModelToggle(currentIds, availableIds, targetId, enabled);
|
|
1874
|
-
this.assertTreeNavigationInactive(session, "
|
|
1895
|
+
this.assertTreeNavigationInactive(session, this.t("server.main.fd81137332d5", "change enabled models"));
|
|
1875
1896
|
if (nextIds !== currentIds)
|
|
1876
1897
|
this.applyEnabledModelScope(session, available, nextIds);
|
|
1877
1898
|
return (await this.enabledModelCatalog(session)).map((entry) => catalogEntryToClientModel(entry));
|
|
@@ -1879,14 +1900,14 @@ export class PiSessionService {
|
|
|
1879
1900
|
}
|
|
1880
1901
|
async setModelScope(ref, mode, managementContext) {
|
|
1881
1902
|
if (managementContext !== undefined)
|
|
1882
|
-
throw new Error("
|
|
1903
|
+
throw new Error(this.t("server.main.2a7cb0e35521", "Enabled model scope cannot be changed in management embed mode"));
|
|
1883
1904
|
await this.assertWritable(ref);
|
|
1884
1905
|
const session = await this.getOrOpen(ref);
|
|
1885
1906
|
return this.runModelScopeMutation(async () => {
|
|
1886
1907
|
const scope = this.modelScopeContext(session);
|
|
1887
1908
|
if (!scope.editable)
|
|
1888
|
-
throw new Error("
|
|
1889
|
-
this.assertTreeNavigationInactive(session, "
|
|
1909
|
+
throw new Error(this.t("server.main.c43b4e3cefe1", "Model availability is controlled by this workspace's .pi/settings.json"));
|
|
1910
|
+
this.assertTreeNavigationInactive(session, this.t("server.main.fd81137332d5", "change enabled models"));
|
|
1890
1911
|
await session.modelRuntime.refresh({ allowNetwork: false });
|
|
1891
1912
|
const available = session.modelRuntime.getAvailableSnapshot();
|
|
1892
1913
|
const availableIds = available.map(modelScopeId);
|
|
@@ -1894,10 +1915,10 @@ export class PiSessionService {
|
|
|
1894
1915
|
if (mode === "current") {
|
|
1895
1916
|
const current = session.model;
|
|
1896
1917
|
if (current === undefined || !availableIds.includes(modelScopeId(current)))
|
|
1897
|
-
throw new Error("
|
|
1918
|
+
throw new Error(this.t("server.main.46b71cd9c032", "The current model is unavailable"));
|
|
1898
1919
|
nextIds = [modelScopeId(current)];
|
|
1899
1920
|
}
|
|
1900
|
-
this.assertTreeNavigationInactive(session, "
|
|
1921
|
+
this.assertTreeNavigationInactive(session, this.t("server.main.fd81137332d5", "change enabled models"));
|
|
1901
1922
|
this.applyEnabledModelScope(session, available, nextIds);
|
|
1902
1923
|
return (await this.enabledModelCatalog(session)).map((entry) => catalogEntryToClientModel(entry));
|
|
1903
1924
|
});
|
|
@@ -1905,15 +1926,15 @@ export class PiSessionService {
|
|
|
1905
1926
|
async setModel(ref, provider, modelId, managementContext) {
|
|
1906
1927
|
await this.assertWritable(ref);
|
|
1907
1928
|
const session = await this.getOrOpen(ref, managementContext);
|
|
1908
|
-
this.assertTreeNavigationInactive(session, "
|
|
1929
|
+
this.assertTreeNavigationInactive(session, this.t("server.main.130226a572df", "change models"));
|
|
1909
1930
|
const candidates = await this.sessionModelCandidates(session);
|
|
1910
|
-
this.assertTreeNavigationInactive(session, "
|
|
1931
|
+
this.assertTreeNavigationInactive(session, this.t("server.main.130226a572df", "change models"));
|
|
1911
1932
|
const model = candidates.find((candidate) => candidate.provider === provider && candidate.id === modelId)
|
|
1912
1933
|
?? session.modelRuntime.getModel(provider, modelId);
|
|
1913
1934
|
if (model === undefined)
|
|
1914
|
-
throw new Error(
|
|
1915
|
-
await this.runSessionEntryMutation(session, "
|
|
1916
|
-
this.publishActivity(session,
|
|
1935
|
+
throw new Error(this.t("model.notFound", "Model not found: {value0}/{value1}", { value0: provider, value1: modelId }));
|
|
1936
|
+
await this.runSessionEntryMutation(session, this.t("server.main.130226a572df", "change models"), () => session.setModel(model));
|
|
1937
|
+
this.publishActivity(session, this.t("activity.model", "Model: {value0}", { value0: model.id }), "idle", model.provider);
|
|
1917
1938
|
this.publishStatus(session);
|
|
1918
1939
|
return this.statusFromSession(session);
|
|
1919
1940
|
}
|
|
@@ -1923,11 +1944,11 @@ export class PiSessionService {
|
|
|
1923
1944
|
const result = await this.runModelScopeMutation(async () => {
|
|
1924
1945
|
await session.modelRuntime.refresh({ allowNetwork: false });
|
|
1925
1946
|
await this.synchronizeSessionModelScope(session, session.modelRuntime.getAvailableSnapshot());
|
|
1926
|
-
return this.runSessionEntryMutation(session, "
|
|
1947
|
+
return this.runSessionEntryMutation(session, this.t("server.main.130226a572df", "change models"), () => session.cycleModel(direction));
|
|
1927
1948
|
});
|
|
1928
1949
|
if (result === undefined)
|
|
1929
|
-
throw new Error(session.scopedModels.length > 0 ? "
|
|
1930
|
-
this.publishActivity(session,
|
|
1950
|
+
throw new Error(session.scopedModels.length > 0 ? this.t("model.oneScoped", "Only one scoped model is available") : this.t("model.oneAvailable", "Only one model is available"));
|
|
1951
|
+
this.publishActivity(session, this.t("activity.model", "Model: {value0}", { value0: result.model.id }), "idle", result.model.provider);
|
|
1931
1952
|
this.publishStatus(session);
|
|
1932
1953
|
return this.statusFromSession(session);
|
|
1933
1954
|
}
|
|
@@ -1938,34 +1959,34 @@ export class PiSessionService {
|
|
|
1938
1959
|
async setThinkingLevel(ref, level, managementContext) {
|
|
1939
1960
|
await this.assertWritable(ref);
|
|
1940
1961
|
const session = await this.getOrOpen(ref, managementContext);
|
|
1941
|
-
this.assertTreeNavigationInactive(session, "
|
|
1962
|
+
this.assertTreeNavigationInactive(session, this.t("session.action.changeThinking", "change the thinking level"));
|
|
1942
1963
|
// pi owns the valid set; validate against the session's live levels rather
|
|
1943
1964
|
// than a hardcoded union so this stays correct if pi changes the set.
|
|
1944
1965
|
const available = session.getAvailableThinkingLevels();
|
|
1945
1966
|
const match = available.find((candidate) => candidate === level);
|
|
1946
1967
|
if (match === undefined)
|
|
1947
|
-
throw new Error(
|
|
1948
|
-
await this.runSessionEntryMutation(session, "
|
|
1968
|
+
throw new Error(this.t("model.invalidThinking", "Invalid thinking level: {value0}", { value0: level }));
|
|
1969
|
+
await this.runSessionEntryMutation(session, this.t("session.action.changeThinking", "change the thinking level"), () => {
|
|
1949
1970
|
session.setThinkingLevel(match);
|
|
1950
1971
|
return Promise.resolve();
|
|
1951
1972
|
});
|
|
1952
|
-
this.publishActivity(session,
|
|
1973
|
+
this.publishActivity(session, this.t("activity.thinking", "Thinking level: {value0}", { value0: session.thinkingLevel }), "idle");
|
|
1953
1974
|
this.publishStatus(session);
|
|
1954
1975
|
return this.statusFromSession(session);
|
|
1955
1976
|
}
|
|
1956
1977
|
async cycleThinkingLevel(ref, managementContext) {
|
|
1957
1978
|
await this.assertWritable(ref);
|
|
1958
1979
|
const session = await this.getOrOpen(ref, managementContext);
|
|
1959
|
-
const level = await this.runSessionEntryMutation(session, "
|
|
1980
|
+
const level = await this.runSessionEntryMutation(session, this.t("session.action.changeThinking", "change the thinking level"), () => Promise.resolve(session.cycleThinkingLevel()));
|
|
1960
1981
|
if (level === undefined)
|
|
1961
|
-
throw new Error("
|
|
1962
|
-
this.publishActivity(session,
|
|
1982
|
+
throw new Error(this.t("model.thinkingUnsupported", "The current model does not support thinking"));
|
|
1983
|
+
this.publishActivity(session, this.t("ui.promptEditor.thinkingLevel", "Thinking level: {thinkingLevel}", { thinkingLevel: level }), "idle");
|
|
1963
1984
|
this.publishStatus(session);
|
|
1964
1985
|
return this.statusFromSession(session);
|
|
1965
1986
|
}
|
|
1966
1987
|
async commands(ref, managementContext) {
|
|
1967
1988
|
const session = await this.getOrOpen(ref, managementContext);
|
|
1968
|
-
const commands =
|
|
1989
|
+
const commands = builtinCommands(this.t);
|
|
1969
1990
|
for (const command of session.extensionRunner.getRegisteredCommands()) {
|
|
1970
1991
|
commands.push({ name: command.invocationName, ...(command.description === undefined ? {} : { description: command.description }), source: "extension" });
|
|
1971
1992
|
}
|
|
@@ -1983,7 +2004,7 @@ export class PiSessionService {
|
|
|
1983
2004
|
return commands.sort((a, b) => a.name.localeCompare(b.name));
|
|
1984
2005
|
}
|
|
1985
2006
|
async prompt(ref, text, streamingBehavior, attachments, options) {
|
|
1986
|
-
const promptText = requirePromptText(text);
|
|
2007
|
+
const promptText = requirePromptText(text, this.t);
|
|
1987
2008
|
// Command-forwarded prompts (e.g. /skill:*) are expanded by the agent, which
|
|
1988
2009
|
// streams the canonical message back. The client doesn't render the raw
|
|
1989
2010
|
// command text, so the server must not echo it either, or it would show up
|
|
@@ -1994,12 +2015,12 @@ export class PiSessionService {
|
|
|
1994
2015
|
const images = (await attachmentsToInlineImages(parsedAttachments)).map((entry) => entry.image);
|
|
1995
2016
|
await this.assertWritable(ref);
|
|
1996
2017
|
const session = await this.getOrOpen(ref, options?.managementContext);
|
|
1997
|
-
this.assertTreeNavigationInactive(session, "
|
|
2018
|
+
this.assertTreeNavigationInactive(session, this.t("server.main.94306b2fc35c", "send a prompt"));
|
|
1998
2019
|
this.maybeGenerateSessionName(session, promptText);
|
|
1999
2020
|
const isQueued = session.isStreaming || session.isCompacting;
|
|
2000
2021
|
const behavior = isQueued ? requestedBehavior ?? "followUp" : undefined;
|
|
2001
2022
|
if (isQueued && images.length === 0 && this.hasQueuedMessageText(session, promptText)) {
|
|
2002
|
-
this.publishActivity(session, "
|
|
2023
|
+
this.publishActivity(session, this.t("activity.duplicateIgnored", "Duplicate queued message ignored"), "active");
|
|
2003
2024
|
this.publishStatus(session);
|
|
2004
2025
|
return;
|
|
2005
2026
|
}
|
|
@@ -2021,15 +2042,15 @@ export class PiSessionService {
|
|
|
2021
2042
|
}
|
|
2022
2043
|
beginPromptSubmission(session, text, behavior, images = [], echoUserMessage = true) {
|
|
2023
2044
|
this.auditUserPrompt(session, text);
|
|
2024
|
-
this.publishActivity(session, behavior === "steer" ? "
|
|
2045
|
+
this.publishActivity(session, behavior === "steer" ? this.t("activity.steerQueued", "Steering message queued") : behavior === "followUp" ? this.t("activity.messageQueued", "Message queued") : this.t("activity.messageReceived", "Message received"), "active");
|
|
2025
2046
|
const eventScope = this.eventScopeForSession(session);
|
|
2026
2047
|
if (behavior === undefined && echoUserMessage)
|
|
2027
2048
|
this.events.publish(session.sessionId, { type: "message.append", message: userMessage(text, images) }, eventScope);
|
|
2028
2049
|
const promptOptions = buildPromptOptions(behavior, images);
|
|
2029
|
-
const promptPromise = this.runSessionEntryMutation(session, "
|
|
2050
|
+
const promptPromise = this.runSessionEntryMutation(session, this.t("server.main.94306b2fc35c", "send a prompt"), () => session.prompt(text, promptOptions));
|
|
2030
2051
|
void promptPromise.catch((error) => {
|
|
2031
2052
|
const message = error instanceof Error ? error.message : String(error);
|
|
2032
|
-
this.publishActivity(session, "
|
|
2053
|
+
this.publishActivity(session, this.t("activity.error", "An error occurred"), "error", message);
|
|
2033
2054
|
this.events.publish(session.sessionId, { type: "session.error", message }, eventScope);
|
|
2034
2055
|
});
|
|
2035
2056
|
return promptPromise;
|
|
@@ -2048,7 +2069,7 @@ export class PiSessionService {
|
|
|
2048
2069
|
}
|
|
2049
2070
|
if (event["type"] === "auto_retry_end" && event["success"] === false && event["finalError"] === "Retry cancelled") {
|
|
2050
2071
|
// Native Pi cancels retry sleep without an aborted assistant message.
|
|
2051
|
-
failure = new DOMException("
|
|
2072
|
+
failure = new DOMException(this.t("server.main.ed6dfd6b55ab", "Initial PI session run was aborted"), "AbortError");
|
|
2052
2073
|
return;
|
|
2053
2074
|
}
|
|
2054
2075
|
const message = event["message"];
|
|
@@ -2068,9 +2089,9 @@ export class PiSessionService {
|
|
|
2068
2089
|
// prompt promise can fulfill even when the final assistant is an error.
|
|
2069
2090
|
const reason = message["stopReason"];
|
|
2070
2091
|
failure = reason === "aborted"
|
|
2071
|
-
? new DOMException("
|
|
2092
|
+
? new DOMException(this.t("server.main.ed6dfd6b55ab", "Initial PI session run was aborted"), "AbortError")
|
|
2072
2093
|
: reason === "error"
|
|
2073
|
-
? new Error(typeof message["errorMessage"] === "string" ? message["errorMessage"] : "
|
|
2094
|
+
? new Error(typeof message["errorMessage"] === "string" ? message["errorMessage"] : this.t("server.main.e08c1380a71f", "Initial PI session run failed"))
|
|
2074
2095
|
: undefined;
|
|
2075
2096
|
});
|
|
2076
2097
|
try {
|
|
@@ -2087,7 +2108,7 @@ export class PiSessionService {
|
|
|
2087
2108
|
const queue = this.compactionPromptQueues.get(sessionKey) ?? [];
|
|
2088
2109
|
queue.push({ kind, text, ...(images.length > 0 ? { images } : {}), ...(echoUserMessage ? {} : { echoUserMessage: false }) });
|
|
2089
2110
|
this.compactionPromptQueues.set(sessionKey, queue);
|
|
2090
|
-
this.publishActivity(session, "
|
|
2111
|
+
this.publishActivity(session, this.t("activity.compactionQueued", "Message queued during compaction"), "active");
|
|
2091
2112
|
this.publishStatus(session);
|
|
2092
2113
|
}
|
|
2093
2114
|
async saveAttachments(ref, attachments, folder, managementContext) {
|
|
@@ -2108,19 +2129,19 @@ export class PiSessionService {
|
|
|
2108
2129
|
await this.assertWritable(ref);
|
|
2109
2130
|
const active = await this.getActive(ref, managementContext);
|
|
2110
2131
|
const { session } = active.runtime;
|
|
2111
|
-
this.assertTreeNavigationInactive(session, "
|
|
2132
|
+
this.assertTreeNavigationInactive(session, this.t("session.action.shell", "run a shell command"));
|
|
2112
2133
|
const isExcluded = text.startsWith("!!");
|
|
2113
2134
|
const command = (isExcluded ? text.slice(2) : text.slice(1)).trim();
|
|
2114
2135
|
if (!command)
|
|
2115
|
-
throw new Error("
|
|
2136
|
+
throw new Error(this.t("shell.usage", "Usage: !<shell command>"));
|
|
2116
2137
|
if (session.isBashRunning)
|
|
2117
|
-
throw new Error("
|
|
2138
|
+
throw new Error(this.t("shell.alreadyRunning", "A bash command is already running"));
|
|
2118
2139
|
const eventScope = active.eventScope;
|
|
2119
|
-
this.publishActivity(session, "
|
|
2140
|
+
this.publishActivity(session, this.t("activity.commandRunning", "Running command"), "active", command);
|
|
2120
2141
|
this.events.publish(session.sessionId, { type: "shell.start", command, excludeFromContext: isExcluded }, eventScope);
|
|
2121
|
-
void this.runSessionEntryMutation(session, "
|
|
2142
|
+
void this.runSessionEntryMutation(session, this.t("session.action.shell", "run a shell command"), () => session.executeBash(command, (chunk) => {
|
|
2122
2143
|
this.events.publish(session.sessionId, { type: "shell.chunk", chunk }, eventScope);
|
|
2123
|
-
this.publishActivity(session, "
|
|
2144
|
+
this.publishActivity(session, this.t("activity.commandRunning", "Running command"), "active", command);
|
|
2124
2145
|
this.publishStatus(session);
|
|
2125
2146
|
}, { excludeFromContext: isExcluded })).then((result) => {
|
|
2126
2147
|
this.events.publish(session.sessionId, {
|
|
@@ -2131,13 +2152,13 @@ export class PiSessionService {
|
|
|
2131
2152
|
truncated: result.truncated,
|
|
2132
2153
|
...(result.fullOutputPath === undefined ? {} : { fullOutputPath: result.fullOutputPath }),
|
|
2133
2154
|
}, eventScope);
|
|
2134
|
-
this.publishActivity(session, "
|
|
2155
|
+
this.publishActivity(session, this.t("activity.commandCompleted", "Command completed"), result.exitCode === 0 ? "idle" : "error", command);
|
|
2135
2156
|
this.publishStatus(session);
|
|
2136
2157
|
}).catch((error) => {
|
|
2137
|
-
const message = error
|
|
2158
|
+
const message = userFacingErrorMessage(error, this.t);
|
|
2138
2159
|
this.events.publish(session.sessionId, { type: "shell.end", output: message, isError: true }, eventScope);
|
|
2139
2160
|
this.events.publish(session.sessionId, { type: "session.error", message }, eventScope);
|
|
2140
|
-
this.publishActivity(session, "
|
|
2161
|
+
this.publishActivity(session, this.t("activity.commandFailed", "Command failed"), "error", message);
|
|
2141
2162
|
this.publishStatus(session);
|
|
2142
2163
|
});
|
|
2143
2164
|
}
|
|
@@ -2153,27 +2174,27 @@ export class PiSessionService {
|
|
|
2153
2174
|
}
|
|
2154
2175
|
async navigateTree(ref, request, managementContext) {
|
|
2155
2176
|
if (request.targetId.trim() === "")
|
|
2156
|
-
throw new Error("
|
|
2177
|
+
throw new Error(this.t("server.main.f39f37acdac1", "Session tree target is required"));
|
|
2157
2178
|
if (this.isTreeExclusiveSessionIdentityActive(sessionIdFromLookup(ref))) {
|
|
2158
|
-
throw new Error("
|
|
2179
|
+
throw new Error(this.t("server.main.e39e6b98e072", "Stop current session activity before navigating the session tree"));
|
|
2159
2180
|
}
|
|
2160
2181
|
await this.assertWritable(ref);
|
|
2161
|
-
const options = sessionTreeNavigationOptions(request);
|
|
2182
|
+
const options = sessionTreeNavigationOptions(request, this.t);
|
|
2162
2183
|
const session = await this.getOrOpen(ref, managementContext);
|
|
2163
2184
|
if (typeof session.navigateTree !== "function")
|
|
2164
|
-
throw new Error("
|
|
2185
|
+
throw new Error(this.t("session.treeUnsupported", "Session tree navigation is not supported by this Pi runtime"));
|
|
2165
2186
|
if (this.hasActiveWork(session))
|
|
2166
|
-
throw new Error("
|
|
2187
|
+
throw new Error(this.t("server.main.e39e6b98e072", "Stop current session activity before navigating the session tree"));
|
|
2167
2188
|
// Acquire synchronously after the active-work check. No leaf-producing work
|
|
2168
2189
|
// may enter this runtime until Pi's potentially asynchronous summary settles.
|
|
2169
2190
|
this.treeNavigations.add(session);
|
|
2170
2191
|
try {
|
|
2171
2192
|
const oldLeafId = session.sessionManager.getLeafId();
|
|
2172
2193
|
if (oldLeafId !== request.expectedLeafId) {
|
|
2173
|
-
throw new Error("
|
|
2194
|
+
throw new Error(this.t("session.treeStale", "The session changed since /tree was opened. Reopen /tree and try again."));
|
|
2174
2195
|
}
|
|
2175
2196
|
const activeEditableTargetParentId = activeEditableTreeTargetParentId(session.sessionManager, request.targetId, oldLeafId);
|
|
2176
|
-
this.publishActivity(session, options.summarize ? "
|
|
2197
|
+
this.publishActivity(session, options.summarize ? this.t("activity.summarizing", "Summarizing branch") : this.t("activity.navigating", "Navigating session tree"), "active");
|
|
2177
2198
|
this.publishStatus(session);
|
|
2178
2199
|
let result = await session.navigateTree(request.targetId, options);
|
|
2179
2200
|
if (activeEditableTargetParentId !== undefined && !result.cancelled
|
|
@@ -2192,7 +2213,7 @@ export class PiSessionService {
|
|
|
2192
2213
|
}
|
|
2193
2214
|
if (result.cancelled) {
|
|
2194
2215
|
if (this.isCurrentActiveSession(session)) {
|
|
2195
|
-
this.publishActivity(session, result.aborted === true ? "
|
|
2216
|
+
this.publishActivity(session, result.aborted === true ? this.t("activity.summaryAborted", "Branch summary aborted") : this.t("activity.navigationCancelled", "Session tree navigation cancelled"), "idle");
|
|
2196
2217
|
}
|
|
2197
2218
|
return { cancelled: true, ...(result.aborted === undefined ? {} : { aborted: result.aborted }) };
|
|
2198
2219
|
}
|
|
@@ -2212,13 +2233,13 @@ export class PiSessionService {
|
|
|
2212
2233
|
this.unpersistedTreeBranchLeaves.set(session, selectedLeafId);
|
|
2213
2234
|
}
|
|
2214
2235
|
if (this.isCurrentActiveSession(session))
|
|
2215
|
-
this.publishActivity(session, "
|
|
2236
|
+
this.publishActivity(session, this.t("activity.navigated", "Session tree changed"), "idle");
|
|
2216
2237
|
return { cancelled: false, ...(result.editorText === undefined ? {} : { editorText: result.editorText }) };
|
|
2217
2238
|
}
|
|
2218
2239
|
catch (error) {
|
|
2219
2240
|
const message = error instanceof Error ? error.message : String(error);
|
|
2220
2241
|
if (this.isCurrentActiveSession(session)) {
|
|
2221
|
-
this.publishActivity(session, "
|
|
2242
|
+
this.publishActivity(session, this.t("server.main.8cae209986b4", "tree navigation failed"), "error", message);
|
|
2222
2243
|
this.events.publish(session.sessionId, { type: "session.error", message }, this.eventScopeForSession(session));
|
|
2223
2244
|
}
|
|
2224
2245
|
throw error;
|
|
@@ -2237,22 +2258,22 @@ export class PiSessionService {
|
|
|
2237
2258
|
}
|
|
2238
2259
|
async forkFromTree(ref, request, managementContext) {
|
|
2239
2260
|
if (request.entryId.trim() === "")
|
|
2240
|
-
throw new Error("
|
|
2261
|
+
throw new Error(this.t("server.main.f10a242f597a", "Session tree entry is required"));
|
|
2241
2262
|
const requestedSessionId = sessionIdFromLookup(ref);
|
|
2242
2263
|
if (this.isTreeExclusiveSessionIdentityActive(requestedSessionId)) {
|
|
2243
|
-
throw new Error("
|
|
2264
|
+
throw new Error(this.t("server.main.3f5b7f08e91c", "Stop current session activity before forking the session tree"));
|
|
2244
2265
|
}
|
|
2245
2266
|
await this.assertWritable(ref);
|
|
2246
2267
|
const session = await this.getOrOpen(ref, managementContext);
|
|
2247
2268
|
if (this.hasActiveWork(session))
|
|
2248
|
-
throw new Error("
|
|
2269
|
+
throw new Error(this.t("server.main.3f5b7f08e91c", "Stop current session activity before forking the session tree"));
|
|
2249
2270
|
if (session.sessionManager.getLeafId() !== request.expectedLeafId) {
|
|
2250
|
-
throw new Error("
|
|
2271
|
+
throw new Error(this.t("session.treeStale", "The session changed since /tree was opened. Reopen /tree and try again."));
|
|
2251
2272
|
}
|
|
2252
2273
|
const sourceSessionId = session.sessionId;
|
|
2253
2274
|
const sourceEventScope = this.eventScopeForSession(session);
|
|
2254
2275
|
const sourceCwd = session.sessionManager.getCwd();
|
|
2255
|
-
this.publishActivity(session, "
|
|
2276
|
+
this.publishActivity(session, this.t("server.main.4de911d3b72a", "forking session from entry"), "active");
|
|
2256
2277
|
this.publishStatus(session);
|
|
2257
2278
|
try {
|
|
2258
2279
|
const result = await this.commandService.forkEntry(session.sessionId, request.entryId, sourceEventScope, {
|
|
@@ -2261,17 +2282,17 @@ export class PiSessionService {
|
|
|
2261
2282
|
if (result.type === "unsupported")
|
|
2262
2283
|
throw new Error(result.message);
|
|
2263
2284
|
if (result.type !== "done")
|
|
2264
|
-
throw new Error("
|
|
2285
|
+
throw new Error(this.t("server.main.0246b6af6843", "Session fork is unavailable"));
|
|
2265
2286
|
if (result.session === undefined) {
|
|
2266
2287
|
if (this.isCurrentActiveSession(session)) {
|
|
2267
|
-
this.publishActivity(session, "
|
|
2288
|
+
this.publishActivity(session, this.t("server.main.1cceead5f414", "fork cancelled"), "idle");
|
|
2268
2289
|
this.publishStatus(session);
|
|
2269
2290
|
}
|
|
2270
2291
|
return { cancelled: true };
|
|
2271
2292
|
}
|
|
2272
2293
|
const forked = this.activeForLookup({ id: result.session.id, cwd: result.session.cwd }, managementContext)?.runtime.session;
|
|
2273
2294
|
if (forked !== undefined && this.isCurrentActiveSession(forked)) {
|
|
2274
|
-
this.publishActivity(forked, "
|
|
2295
|
+
this.publishActivity(forked, this.t("command.forked", "Session forked"), "idle");
|
|
2275
2296
|
this.publishStatus(forked);
|
|
2276
2297
|
}
|
|
2277
2298
|
if (result.session.id !== sourceSessionId)
|
|
@@ -2285,7 +2306,7 @@ export class PiSessionService {
|
|
|
2285
2306
|
catch (error) {
|
|
2286
2307
|
const message = error instanceof Error ? error.message : String(error);
|
|
2287
2308
|
if (this.isCurrentActiveSession(session)) {
|
|
2288
|
-
this.publishActivity(session, "
|
|
2309
|
+
this.publishActivity(session, this.t("server.main.08e8f3ea9eaa", "fork failed"), "error", message);
|
|
2289
2310
|
this.events.publish(session.sessionId, { type: "session.error", message }, sourceEventScope);
|
|
2290
2311
|
this.publishStatus(session);
|
|
2291
2312
|
}
|
|
@@ -2296,7 +2317,7 @@ export class PiSessionService {
|
|
|
2296
2317
|
const sessionKey = activeSessionKey(sessionId, eventScope);
|
|
2297
2318
|
if (this.activities.get(sessionKey)?.phase === "active") {
|
|
2298
2319
|
const at = new Date().toISOString();
|
|
2299
|
-
const stored = { phase: "idle", label: "
|
|
2320
|
+
const stored = { phase: "idle", label: this.t("activity.idle", "Idle"), at };
|
|
2300
2321
|
this.activities.set(sessionKey, stored);
|
|
2301
2322
|
const activity = { sessionId, ...stored };
|
|
2302
2323
|
this.events.publish(sessionId, { type: "activity.update", activity }, eventScope);
|
|
@@ -2308,7 +2329,7 @@ export class PiSessionService {
|
|
|
2308
2329
|
async archive(ref, managementContext) {
|
|
2309
2330
|
const active = await this.getActive(ref, managementContext);
|
|
2310
2331
|
const session = active.runtime.session;
|
|
2311
|
-
await this.runTreeExclusiveOperation([{ sessionId: session.sessionId, session, runtime: active.runtime }], "
|
|
2332
|
+
await this.runTreeExclusiveOperation([{ sessionId: session.sessionId, session, runtime: active.runtime }], this.t("session.stopBeforeArchive", "Stop current session activity before archiving"), async () => {
|
|
2312
2333
|
const archiveInput = await this.archiveInputForSession(session);
|
|
2313
2334
|
await this.closeActiveSession(active);
|
|
2314
2335
|
await this.archiveStore.archive(archiveInput);
|
|
@@ -2337,7 +2358,7 @@ export class PiSessionService {
|
|
|
2337
2358
|
const listed = findListedSessionForBulkRef(sessionContext, ref);
|
|
2338
2359
|
const resolvedSessionId = active?.runtime.session.sessionId ?? listed?.id ?? ref.id;
|
|
2339
2360
|
if (active !== undefined && this.hasActiveWork(active.runtime.session)) {
|
|
2340
|
-
failures.push({ sessionId: resolvedSessionId, error: "Stop current session activity before archiving" });
|
|
2361
|
+
failures.push({ sessionId: resolvedSessionId, error: this.t("session.stopBeforeArchive", "Stop current session activity before archiving") });
|
|
2341
2362
|
continue;
|
|
2342
2363
|
}
|
|
2343
2364
|
try {
|
|
@@ -2345,21 +2366,21 @@ export class PiSessionService {
|
|
|
2345
2366
|
planItems.push({ input: archiveInputFromListEntry(listed), ...(active === undefined ? {} : { active }) });
|
|
2346
2367
|
}
|
|
2347
2368
|
else if (active !== undefined) {
|
|
2348
|
-
planItems.push({ input: archiveInputFromActiveSession(active.runtime.session), active });
|
|
2369
|
+
planItems.push({ input: archiveInputFromActiveSession(active.runtime.session, this.t), active });
|
|
2349
2370
|
}
|
|
2350
2371
|
else {
|
|
2351
|
-
failures.push({ sessionId: ref.id, error: "Session not found" });
|
|
2372
|
+
failures.push({ sessionId: ref.id, error: this.t("session.notFound", "Session not found") });
|
|
2352
2373
|
}
|
|
2353
2374
|
}
|
|
2354
2375
|
catch (error) {
|
|
2355
|
-
failures.push({ sessionId: resolvedSessionId, error: errorMessage(error) });
|
|
2376
|
+
failures.push({ sessionId: resolvedSessionId, error: errorMessage(error, this.t) });
|
|
2356
2377
|
}
|
|
2357
2378
|
}
|
|
2358
2379
|
const readyPlanItems = [];
|
|
2359
2380
|
for (const item of planItems) {
|
|
2360
2381
|
const active = item.active ?? this.activeForLookup(bulkRefToLookup({ id: item.input.sessionId, cwd: item.input.cwd }), managementContext);
|
|
2361
2382
|
if (active !== undefined && this.hasActiveWork(active.runtime.session)) {
|
|
2362
|
-
failures.push({ sessionId: item.input.sessionId, error: "Stop current session activity before archiving" });
|
|
2383
|
+
failures.push({ sessionId: item.input.sessionId, error: this.t("session.stopBeforeArchive", "Stop current session activity before archiving") });
|
|
2363
2384
|
continue;
|
|
2364
2385
|
}
|
|
2365
2386
|
readyPlanItems.push(active === undefined ? item : { ...item, active });
|
|
@@ -2369,7 +2390,7 @@ export class PiSessionService {
|
|
|
2369
2390
|
await this.runTreeExclusiveOperation(readyPlanItems.map(({ input, active }) => ({
|
|
2370
2391
|
sessionId: input.sessionId,
|
|
2371
2392
|
...(active === undefined ? {} : { session: active.runtime.session, runtime: active.runtime }),
|
|
2372
|
-
})), "Stop current session activity before archiving", async () => {
|
|
2393
|
+
})), this.t("session.stopBeforeArchive", "Stop current session activity before archiving"), async () => {
|
|
2373
2394
|
for (const item of readyPlanItems) {
|
|
2374
2395
|
try {
|
|
2375
2396
|
if (item.active !== undefined)
|
|
@@ -2379,7 +2400,7 @@ export class PiSessionService {
|
|
|
2379
2400
|
readyInputs.push(item.input);
|
|
2380
2401
|
}
|
|
2381
2402
|
catch (error) {
|
|
2382
|
-
failures.push({ sessionId: item.input.sessionId, error: errorMessage(error) });
|
|
2403
|
+
failures.push({ sessionId: item.input.sessionId, error: errorMessage(error, this.t) });
|
|
2383
2404
|
}
|
|
2384
2405
|
}
|
|
2385
2406
|
try {
|
|
@@ -2389,7 +2410,7 @@ export class PiSessionService {
|
|
|
2389
2410
|
}
|
|
2390
2411
|
catch (error) {
|
|
2391
2412
|
for (const input of readyInputs)
|
|
2392
|
-
failures.push({ sessionId: input.sessionId, error: errorMessage(error) });
|
|
2413
|
+
failures.push({ sessionId: input.sessionId, error: errorMessage(error, this.t) });
|
|
2393
2414
|
}
|
|
2394
2415
|
});
|
|
2395
2416
|
await this.forgetUnreadSessions(unreadArchivedIdentities, eventScopeFromManagementContext(managementContext));
|
|
@@ -2403,16 +2424,16 @@ export class PiSessionService {
|
|
|
2403
2424
|
async archiveTree(ref, managementContext) {
|
|
2404
2425
|
const session = await this.getOrOpen(ref, managementContext);
|
|
2405
2426
|
const catalog = await this.workspaceArchiveCandidates(session.sessionManager.getCwd());
|
|
2406
|
-
const root = findArchiveCandidateByIdOrPrefix(catalog, session.sessionId) ?? archiveCandidateFromActiveSession(session, false);
|
|
2427
|
+
const root = findArchiveCandidateByIdOrPrefix(catalog, session.sessionId) ?? archiveCandidateFromActiveSession(session, false, this.t);
|
|
2407
2428
|
const plan = planSessionArchiveTree(root, catalog);
|
|
2408
2429
|
const busy = plan.targets.map((target) => target.activeSession).find((target) => target !== undefined && this.hasActiveWork(target));
|
|
2409
2430
|
if (busy !== undefined)
|
|
2410
|
-
throw new Error(
|
|
2411
|
-
const archiveInputs = plan.unarchivedTargets.map((target) => archiveInputFromCandidate(target));
|
|
2431
|
+
throw new Error(this.t("session.stopBeforeNamedArchive", "Stop current session activity before archiving {value0}", { value0: sessionDisplayName(busy) }));
|
|
2432
|
+
const archiveInputs = plan.unarchivedTargets.map((target) => archiveInputFromCandidate(target, this.t));
|
|
2412
2433
|
await this.runTreeExclusiveOperation(plan.unarchivedTargets.map((target) => ({
|
|
2413
2434
|
sessionId: target.id,
|
|
2414
2435
|
...(target.activeSession === undefined ? {} : { session: target.activeSession }),
|
|
2415
|
-
})),
|
|
2436
|
+
})), this.t("session.stopBeforeNamedArchive", "Stop current session activity before archiving {value0}", { value0: sessionDisplayName(session) }), async () => {
|
|
2416
2437
|
for (const target of plan.targets) {
|
|
2417
2438
|
if (target.archived)
|
|
2418
2439
|
this.publishNotificationMutations(this.notificationsForScope(eventScopeFromManagementContext(managementContext)).clearSession(target.id, "archive"));
|
|
@@ -2432,7 +2453,7 @@ export class PiSessionService {
|
|
|
2432
2453
|
async restore(ref, managementContext) {
|
|
2433
2454
|
const archived = await this.getArchived(ref);
|
|
2434
2455
|
if (archived === undefined)
|
|
2435
|
-
throw new Error("
|
|
2456
|
+
throw new Error(this.t("session.notFound", "Session not found"));
|
|
2436
2457
|
await this.closeActiveInScope(archived.sessionId, eventScopeFromManagementContext(managementContext));
|
|
2437
2458
|
await this.archiveStore.restore(archived.sessionId);
|
|
2438
2459
|
await this.forgetUnreadSessions([archived], eventScopeFromManagementContext(managementContext));
|
|
@@ -2440,16 +2461,16 @@ export class PiSessionService {
|
|
|
2440
2461
|
async deleteArchived(ref, managementContext) {
|
|
2441
2462
|
const record = await this.getArchived(ref);
|
|
2442
2463
|
if (record === undefined)
|
|
2443
|
-
throw new Error("
|
|
2464
|
+
throw new Error(this.t("session.archiveNotFound", "Archived session not found"));
|
|
2444
2465
|
if (this.archiveStore.deleteArchived === undefined)
|
|
2445
|
-
throw new Error("
|
|
2466
|
+
throw new Error(this.t("session.archiveDeleteUnsupported", "The archive store does not support deletion"));
|
|
2446
2467
|
await this.closeActiveInScope(record.sessionId, eventScopeFromManagementContext(managementContext));
|
|
2447
2468
|
await this.archiveStore.deleteArchived(record.sessionId);
|
|
2448
2469
|
await this.forgetUnreadSessions([record], eventScopeFromManagementContext(managementContext));
|
|
2449
2470
|
}
|
|
2450
2471
|
async deleteArchivedMany(refs, managementContext) {
|
|
2451
2472
|
if (this.archiveStore.deleteArchived === undefined && this.archiveStore.deleteArchivedMany === undefined)
|
|
2452
|
-
throw new Error("
|
|
2473
|
+
throw new Error(this.t("session.archiveDeleteUnsupported", "The archive store does not support deletion"));
|
|
2453
2474
|
const uniqueRefs = uniqueBulkSessionRefs(refs);
|
|
2454
2475
|
const archivedRecords = await this.archiveStore.list();
|
|
2455
2476
|
const failures = [];
|
|
@@ -2457,12 +2478,12 @@ export class PiSessionService {
|
|
|
2457
2478
|
for (const ref of uniqueRefs) {
|
|
2458
2479
|
const record = findArchivedRecordForBulkRef(archivedRecords, ref);
|
|
2459
2480
|
if (record === undefined) {
|
|
2460
|
-
failures.push({ sessionId: ref.id, error: "Archived session not found" });
|
|
2481
|
+
failures.push({ sessionId: ref.id, error: this.t("session.archiveNotFound", "Archived session not found") });
|
|
2461
2482
|
continue;
|
|
2462
2483
|
}
|
|
2463
2484
|
const active = this.activeForLookup({ id: record.sessionId, cwd: record.cwd }, managementContext);
|
|
2464
2485
|
if (active !== undefined && this.hasActiveWork(active.runtime.session)) {
|
|
2465
|
-
failures.push({ sessionId: record.sessionId, error: "Stop current session activity before deleting archived session" });
|
|
2486
|
+
failures.push({ sessionId: record.sessionId, error: this.t("session.stopBeforeDeleteArchive", "Stop current session activity before deleting archived session") });
|
|
2466
2487
|
continue;
|
|
2467
2488
|
}
|
|
2468
2489
|
planItems.push({ record });
|
|
@@ -2474,7 +2495,7 @@ export class PiSessionService {
|
|
|
2474
2495
|
readyRecords.push(item.record);
|
|
2475
2496
|
}
|
|
2476
2497
|
catch (error) {
|
|
2477
|
-
failures.push({ sessionId: item.record.sessionId, error: errorMessage(error) });
|
|
2498
|
+
failures.push({ sessionId: item.record.sessionId, error: errorMessage(error, this.t) });
|
|
2478
2499
|
}
|
|
2479
2500
|
}
|
|
2480
2501
|
const deleteIds = readyRecords.map((record) => record.sessionId);
|
|
@@ -2484,7 +2505,7 @@ export class PiSessionService {
|
|
|
2484
2505
|
}
|
|
2485
2506
|
catch (error) {
|
|
2486
2507
|
for (const sessionId of deleteIds)
|
|
2487
|
-
failures.push({ sessionId, error: errorMessage(error) });
|
|
2508
|
+
failures.push({ sessionId, error: errorMessage(error, this.t) });
|
|
2488
2509
|
}
|
|
2489
2510
|
const deletedIdSet = new Set(deletedSessionIds);
|
|
2490
2511
|
await this.forgetUnreadSessions(readyRecords.filter((record) => deletedIdSet.has(record.sessionId)), eventScopeFromManagementContext(managementContext));
|
|
@@ -2500,11 +2521,11 @@ export class PiSessionService {
|
|
|
2500
2521
|
const active = await this.getActive(ref, managementContext);
|
|
2501
2522
|
const session = active.runtime.session;
|
|
2502
2523
|
if (this.hasActiveWork(session))
|
|
2503
|
-
throw new Error("
|
|
2524
|
+
throw new Error(this.t("session.stopBeforeReload", "Stop current session activity before reloading"));
|
|
2504
2525
|
const sessionFile = session.sessionFile;
|
|
2505
2526
|
if (sessionFile === undefined || sessionFile === "")
|
|
2506
|
-
throw new Error("
|
|
2507
|
-
const reopenedSession = await this.runTreeExclusiveOperation([{ sessionId: session.sessionId, session, runtime: active.runtime }], "
|
|
2527
|
+
throw new Error(this.t("session.notPersisted", "Session has not been persisted"));
|
|
2528
|
+
const reopenedSession = await this.runTreeExclusiveOperation([{ sessionId: session.sessionId, session, runtime: active.runtime }], this.t("session.stopBeforeReload", "Stop current session activity before reloading"), async () => {
|
|
2508
2529
|
const priorGeneration = this.notificationGenerationBySession.get(session);
|
|
2509
2530
|
const { sessionId, cwd } = notificationIdentityForSession(session);
|
|
2510
2531
|
let candidateGeneration;
|
|
@@ -2537,7 +2558,7 @@ export class PiSessionService {
|
|
|
2537
2558
|
const session = await this.getOrOpen(ref, managementContext);
|
|
2538
2559
|
const sessionFile = session.sessionFile;
|
|
2539
2560
|
if (sessionFile === undefined || sessionFile === "")
|
|
2540
|
-
throw new Error("
|
|
2561
|
+
throw new Error(this.t("session.notPersisted", "Session has not been persisted"));
|
|
2541
2562
|
clearSessionFileParent(sessionFile);
|
|
2542
2563
|
this.sessionManager.invalidateSessionFile(sessionFile);
|
|
2543
2564
|
clearParentSessionHeader(session.sessionManager);
|
|
@@ -2553,11 +2574,11 @@ export class PiSessionService {
|
|
|
2553
2574
|
this.abortRunScopedExtensionDialogs(active.runtime.session);
|
|
2554
2575
|
try {
|
|
2555
2576
|
await this.abortSessionOperations(active.runtime.session);
|
|
2556
|
-
this.publishActivity(active.runtime.session, "
|
|
2577
|
+
this.publishActivity(active.runtime.session, this.t("activity.stopped", "Stopped"), "idle");
|
|
2557
2578
|
}
|
|
2558
2579
|
catch (error) {
|
|
2559
|
-
const message = error
|
|
2560
|
-
this.publishActivity(active.runtime.session, "
|
|
2580
|
+
const message = userFacingErrorMessage(error, this.t);
|
|
2581
|
+
this.publishActivity(active.runtime.session, this.t("activity.stopFailed", "Stop failed"), "error", message);
|
|
2561
2582
|
throw error;
|
|
2562
2583
|
}
|
|
2563
2584
|
finally {
|
|
@@ -2607,7 +2628,7 @@ export class PiSessionService {
|
|
|
2607
2628
|
if (this.archiveStore.deleteArchivedMany !== undefined)
|
|
2608
2629
|
return this.archiveStore.deleteArchivedMany(sessionIds);
|
|
2609
2630
|
if (this.archiveStore.deleteArchived === undefined)
|
|
2610
|
-
throw new Error("
|
|
2631
|
+
throw new Error(this.t("session.archiveDeleteUnsupported", "The archive store does not support deletion"));
|
|
2611
2632
|
for (const sessionId of sessionIds)
|
|
2612
2633
|
await this.archiveStore.deleteArchived(sessionId);
|
|
2613
2634
|
return [...sessionIds];
|
|
@@ -2661,11 +2682,11 @@ export class PiSessionService {
|
|
|
2661
2682
|
const cwd = session.sessionManager.getCwd();
|
|
2662
2683
|
const sessionFile = session.sessionFile;
|
|
2663
2684
|
if (sessionFile === undefined || sessionFile === "")
|
|
2664
|
-
throw new Error("
|
|
2685
|
+
throw new Error(this.t("session.notPersisted", "Session has not been persisted"));
|
|
2665
2686
|
const listed = (await this.sessionManager.list(cwd)).find((candidate) => candidate.id === session.sessionId);
|
|
2666
2687
|
if (listed !== undefined)
|
|
2667
2688
|
return archiveInputFromListEntry(listed);
|
|
2668
|
-
return archiveInputFromActiveSession(session);
|
|
2689
|
+
return archiveInputFromActiveSession(session, this.t);
|
|
2669
2690
|
}
|
|
2670
2691
|
async workspaceArchiveCandidates(cwd) {
|
|
2671
2692
|
const [sessions, archivedRecords] = await Promise.all([this.sessionManager.list(cwd), this.archiveStore.list()]);
|
|
@@ -2697,7 +2718,7 @@ export class PiSessionService {
|
|
|
2697
2718
|
if (session.sessionManager.getCwd() !== cwd || archivedById.has(session.sessionId))
|
|
2698
2719
|
continue;
|
|
2699
2720
|
const existing = candidates.get(session.sessionId);
|
|
2700
|
-
candidates.set(session.sessionId, { ...(existing ?? archiveCandidateFromActiveSession(session, false)), activeSession: session });
|
|
2721
|
+
candidates.set(session.sessionId, { ...(existing ?? archiveCandidateFromActiveSession(session, false, this.t)), activeSession: session });
|
|
2701
2722
|
}
|
|
2702
2723
|
return [...candidates.values()];
|
|
2703
2724
|
}
|
|
@@ -2794,7 +2815,7 @@ export class PiSessionService {
|
|
|
2794
2815
|
}
|
|
2795
2816
|
async assertWritable(ref) {
|
|
2796
2817
|
if (await this.getArchived(ref) !== undefined)
|
|
2797
|
-
throw new Error("
|
|
2818
|
+
throw new Error(this.t("session.archiveReadOnly", "Archived sessions are read-only. Restore the session to continue."));
|
|
2798
2819
|
}
|
|
2799
2820
|
async getOrOpen(ref, managementContext) {
|
|
2800
2821
|
return (await this.getActive(ref, managementContext)).runtime.session;
|
|
@@ -2881,7 +2902,7 @@ export class PiSessionService {
|
|
|
2881
2902
|
? await this.sessionManager.resolveSessionFile(ref.cwd, ref.id)
|
|
2882
2903
|
: findSessionByIdOrPrefix(await this.sessionManager.listAll(), ref);
|
|
2883
2904
|
if (!match)
|
|
2884
|
-
throw new Error("
|
|
2905
|
+
throw new Error(this.t("routing.sessionNotFound", "Session not found"));
|
|
2885
2906
|
return this.openPending(match.id, match.cwd, managementContext, () => this.sessionManager.open(match.path));
|
|
2886
2907
|
}
|
|
2887
2908
|
openPending(sessionId, cwd, managementContext, openSessionManager, options = {}) {
|
|
@@ -2949,13 +2970,13 @@ export class PiSessionService {
|
|
|
2949
2970
|
getActiveForCommand(sessionId, eventScope) {
|
|
2950
2971
|
const active = eventScope === undefined ? this.activeForSessionId(sessionId) : this.activeForSessionIdAndScope(sessionId, eventScope);
|
|
2951
2972
|
if (active === undefined)
|
|
2952
|
-
throw new Error(
|
|
2973
|
+
throw new Error(this.t("session.notActive", "Session not active: {sessionId}", { sessionId }));
|
|
2953
2974
|
return Promise.resolve(active);
|
|
2954
2975
|
}
|
|
2955
2976
|
submitCommandPrompt(sessionId, text, eventScope) {
|
|
2956
2977
|
const active = this.activeForSessionIdAndScope(sessionId, eventScope);
|
|
2957
2978
|
if (active === undefined)
|
|
2958
|
-
throw new Error(
|
|
2979
|
+
throw new Error(this.t("session.notActive", "Session not active: {sessionId}", { sessionId }));
|
|
2959
2980
|
return this.submitPrompt(active.runtime.session, text, undefined, [], false);
|
|
2960
2981
|
}
|
|
2961
2982
|
/**
|
|
@@ -3011,7 +3032,7 @@ export class PiSessionService {
|
|
|
3011
3032
|
mode: "rpc",
|
|
3012
3033
|
onError: (error) => {
|
|
3013
3034
|
const message = `${error.extensionPath}: ${error.error}`;
|
|
3014
|
-
this.publishActivity(session, "
|
|
3035
|
+
this.publishActivity(session, this.t("activity.extensionError", "Extension error"), "error", message);
|
|
3015
3036
|
this.events.publish(session.sessionId, { type: "session.error", message }, owner.eventScope);
|
|
3016
3037
|
},
|
|
3017
3038
|
});
|
|
@@ -3047,7 +3068,7 @@ export class PiSessionService {
|
|
|
3047
3068
|
if (eventType === "message_start" && isRecord(event) && getString(event["message"], "role") === "user") {
|
|
3048
3069
|
void this.voidOpenAskForUserMessage(session).catch((error) => {
|
|
3049
3070
|
const message = error instanceof Error ? error.message : String(error);
|
|
3050
|
-
this.publishActivity(session, "
|
|
3071
|
+
this.publishActivity(session, this.t("activity.extensionError", "Extension error"), "error", message);
|
|
3051
3072
|
this.events.publish(session.sessionId, { type: "session.error", message }, active.eventScope);
|
|
3052
3073
|
});
|
|
3053
3074
|
}
|
|
@@ -3187,7 +3208,7 @@ export class PiSessionService {
|
|
|
3187
3208
|
this.events.publish(session.sessionId, {
|
|
3188
3209
|
type: "command.output",
|
|
3189
3210
|
level: "error",
|
|
3190
|
-
message:
|
|
3211
|
+
message: this.t("session.authRemoved", "Authentication for {provider}/{model} was removed. Use /model to select another model.", { provider: model.provider, model: model.id }),
|
|
3191
3212
|
}, eventScope);
|
|
3192
3213
|
}
|
|
3193
3214
|
clearAuthLossWarningsForSession(sessionId) {
|
|
@@ -3227,18 +3248,18 @@ export class PiSessionService {
|
|
|
3227
3248
|
}
|
|
3228
3249
|
activityLabelFromStatus(session) {
|
|
3229
3250
|
if (this.treeNavigations.has(session))
|
|
3230
|
-
return "
|
|
3251
|
+
return this.t("activity.navigating", "Navigating session tree");
|
|
3231
3252
|
if (this.isSessionEntryMutationActive(session))
|
|
3232
|
-
return "
|
|
3253
|
+
return this.t("activity.updating", "Updating session");
|
|
3233
3254
|
if (session.isCompacting)
|
|
3234
|
-
return "
|
|
3255
|
+
return this.t("activity.compacting", "Compacting context");
|
|
3235
3256
|
if (session.isBashRunning)
|
|
3236
|
-
return "
|
|
3257
|
+
return this.t("activity.commandRunning", "Running command");
|
|
3237
3258
|
if (session.isStreaming)
|
|
3238
|
-
return "
|
|
3259
|
+
return this.t("activity.agentRunning", "Agent running");
|
|
3239
3260
|
if (this.pendingMessageCount(session) > 0)
|
|
3240
|
-
return "
|
|
3241
|
-
return "
|
|
3261
|
+
return this.t("activity.queued", "Queued");
|
|
3262
|
+
return this.t("activity.working", "Working");
|
|
3242
3263
|
}
|
|
3243
3264
|
hasActiveWork(session) {
|
|
3244
3265
|
return this.treeNavigations.has(session)
|
|
@@ -3304,7 +3325,7 @@ export class PiSessionService {
|
|
|
3304
3325
|
}
|
|
3305
3326
|
assertTreeNavigationInactive(session, action) {
|
|
3306
3327
|
if (this.treeNavigations.has(session))
|
|
3307
|
-
throw new Error(
|
|
3328
|
+
throw new Error(this.t("server.main.0c70757b6815", "Cannot {arg0} while session tree navigation is active", { arg0: action }));
|
|
3308
3329
|
}
|
|
3309
3330
|
async runSessionEntryMutation(session, action, operation) {
|
|
3310
3331
|
this.beginSessionEntryMutation(session, action);
|
|
@@ -3336,56 +3357,56 @@ export class PiSessionService {
|
|
|
3336
3357
|
if (eventType === undefined)
|
|
3337
3358
|
return;
|
|
3338
3359
|
if (eventType === "agent_start") {
|
|
3339
|
-
this.publishActivity(session, "
|
|
3360
|
+
this.publishActivity(session, this.t("activity.agentRunning", "Agent running"), "active");
|
|
3340
3361
|
return;
|
|
3341
3362
|
}
|
|
3342
3363
|
if (eventType === "agent_end") {
|
|
3343
|
-
this.publishActivity(session, "
|
|
3364
|
+
this.publishActivity(session, this.t("activity.idle", "Idle"), "idle");
|
|
3344
3365
|
const eventScope = this.eventScopeForSession(session);
|
|
3345
3366
|
const sessionKey = activeSessionKey(session.sessionId, eventScope);
|
|
3346
3367
|
this.activities.scheduleSettledRefresh(sessionKey, () => {
|
|
3347
3368
|
if (this.active.get(sessionKey)?.runtime.session !== session)
|
|
3348
3369
|
return;
|
|
3349
|
-
this.publishActivity(session, "
|
|
3370
|
+
this.publishActivity(session, this.t("activity.idle", "Idle"), "idle");
|
|
3350
3371
|
this.publishStatus(session);
|
|
3351
3372
|
}, 250);
|
|
3352
3373
|
return;
|
|
3353
3374
|
}
|
|
3354
3375
|
if (eventType === "turn_end") {
|
|
3355
|
-
this.publishActivity(session, "
|
|
3376
|
+
this.publishActivity(session, this.t("activity.turnCompleted", "Turn completed"), "idle");
|
|
3356
3377
|
return;
|
|
3357
3378
|
}
|
|
3358
3379
|
if (eventType === "message_start") {
|
|
3359
|
-
this.publishActivity(session, "
|
|
3380
|
+
this.publishActivity(session, this.t("activity.messageStarted", "Message started"), "active");
|
|
3360
3381
|
return;
|
|
3361
3382
|
}
|
|
3362
3383
|
if (eventType === "message_end") {
|
|
3363
|
-
this.publishActivity(session, "
|
|
3384
|
+
this.publishActivity(session, this.t("activity.messageCompleted", "Message completed"), "idle");
|
|
3364
3385
|
return;
|
|
3365
3386
|
}
|
|
3366
3387
|
if (eventType === "message_update") {
|
|
3367
|
-
this.publishActivity(session, "
|
|
3388
|
+
this.publishActivity(session, this.t("activity.receiving", "Receiving reply"), "active");
|
|
3368
3389
|
return;
|
|
3369
3390
|
}
|
|
3370
3391
|
if (eventType === "tool_execution_start") {
|
|
3371
|
-
this.publishActivity(session, "
|
|
3392
|
+
this.publishActivity(session, this.t("activity.toolRunning", "Running tool"), "active", getString(event, "toolName"));
|
|
3372
3393
|
return;
|
|
3373
3394
|
}
|
|
3374
3395
|
if (eventType === "tool_execution_end") {
|
|
3375
3396
|
const isError = getBoolean(event, "isError") === true;
|
|
3376
|
-
this.publishActivity(session, isError ? "
|
|
3397
|
+
this.publishActivity(session, isError ? this.t("activity.toolFailed", "Tool failed") : this.t("activity.toolCompleted", "Tool completed"), isError ? "error" : "idle", getString(event, "toolName"));
|
|
3377
3398
|
return;
|
|
3378
3399
|
}
|
|
3379
3400
|
if (eventType === "bash_execution_start") {
|
|
3380
|
-
this.publishActivity(session, "
|
|
3401
|
+
this.publishActivity(session, this.t("activity.commandRunning", "Running command"), "active");
|
|
3381
3402
|
return;
|
|
3382
3403
|
}
|
|
3383
3404
|
if (eventType === "bash_execution_end") {
|
|
3384
|
-
this.publishActivity(session, "
|
|
3405
|
+
this.publishActivity(session, this.t("activity.commandCompleted", "Command completed"), "idle");
|
|
3385
3406
|
return;
|
|
3386
3407
|
}
|
|
3387
3408
|
if (this.hasActiveWork(session))
|
|
3388
|
-
this.publishActivity(session, "
|
|
3409
|
+
this.publishActivity(session, this.t("activity.working", "Working"), "active");
|
|
3389
3410
|
}
|
|
3390
3411
|
auditToolExecutionEvent(active, event) {
|
|
3391
3412
|
const eventType = getString(event, "type");
|
|
@@ -3482,7 +3503,7 @@ export class PiSessionService {
|
|
|
3482
3503
|
const sessionId = sessionManager.getSessionId();
|
|
3483
3504
|
if (sessionId === "")
|
|
3484
3505
|
return { report: noop, end: noop };
|
|
3485
|
-
const label = intent === "create" ? "
|
|
3506
|
+
const label = intent === "create" ? this.t("activity.creating", "Creating session") : this.t("activity.opening", "Opening session");
|
|
3486
3507
|
return {
|
|
3487
3508
|
report: (phase) => { this.publishStartupProgress(sessionId, startupToken, label, "active", this.startupDetail(phase), eventScope); },
|
|
3488
3509
|
end: () => {
|
|
@@ -3490,13 +3511,13 @@ export class PiSessionService {
|
|
|
3490
3511
|
// is the truth about this session and must survive the clear.
|
|
3491
3512
|
if (this.activities.get(activeSessionKey(sessionId, eventScope)) !== undefined)
|
|
3492
3513
|
return;
|
|
3493
|
-
this.publishStartupProgress(sessionId, startupToken, "
|
|
3514
|
+
this.publishStartupProgress(sessionId, startupToken, this.t("activity.idle", "Idle"), "idle", undefined, eventScope);
|
|
3494
3515
|
},
|
|
3495
3516
|
};
|
|
3496
3517
|
}
|
|
3497
3518
|
startupDetail(phase) {
|
|
3498
3519
|
return this.catalogRefreshStatus?.isRefreshInFlight() === true
|
|
3499
|
-
? `${phase} · ${
|
|
3520
|
+
? `${phase} · ${this.t("startup.catalogRefresh", "Provider model catalog is refreshing")}`
|
|
3500
3521
|
: phase;
|
|
3501
3522
|
}
|
|
3502
3523
|
/**
|
|
@@ -3556,7 +3577,7 @@ export class PiSessionService {
|
|
|
3556
3577
|
if (current?.phase !== "active" || this.hasActiveWork(session))
|
|
3557
3578
|
return;
|
|
3558
3579
|
const at = new Date().toISOString();
|
|
3559
|
-
const stored = { phase: "idle", label: "
|
|
3580
|
+
const stored = { phase: "idle", label: this.t("activity.idle", "Idle"), at };
|
|
3560
3581
|
this.activities.set(activeSessionKey(session.sessionId, eventScope), stored);
|
|
3561
3582
|
const activity = { sessionId: session.sessionId, ...stored };
|
|
3562
3583
|
this.events.publish(session.sessionId, { type: "activity.update", activity }, eventScope);
|
|
@@ -3635,7 +3656,7 @@ export class PiSessionService {
|
|
|
3635
3656
|
&& this.activityMarker.isActiveElsewhere(session.sessionFile)) {
|
|
3636
3657
|
warnings.push({
|
|
3637
3658
|
severity: "info",
|
|
3638
|
-
message: "
|
|
3659
|
+
message: this.t("server.main.b80808e6ef11", "Recently active in another PI-WEB instance. Avoid working on this session in both instances at once."),
|
|
3639
3660
|
source: "PI-WEB",
|
|
3640
3661
|
});
|
|
3641
3662
|
}
|
|
@@ -3715,7 +3736,7 @@ export class PiSessionService {
|
|
|
3715
3736
|
return result.snapshot;
|
|
3716
3737
|
}
|
|
3717
3738
|
async deliverSubsessionNotification(session, notification) {
|
|
3718
|
-
await this.runSessionEntryMutation(session, "
|
|
3739
|
+
await this.runSessionEntryMutation(session, this.t("session.action.notify", "deliver a subsession notification"), () => session.sendCustomMessage({ customType: SUBSESSION_NOTIFICATION_CUSTOM_TYPE, content: notification.text, display: true, details: { sessionId: notification.childId } }, { triggerTurn: true, deliverAs: "followUp" }));
|
|
3719
3740
|
this.publishStatus(session);
|
|
3720
3741
|
}
|
|
3721
3742
|
logSubsessionNotificationFailure(parentId, childId, error) {
|
|
@@ -3723,9 +3744,9 @@ export class PiSessionService {
|
|
|
3723
3744
|
}
|
|
3724
3745
|
async reloadSessionRuntime(session) {
|
|
3725
3746
|
if (this.hasActiveWork(session))
|
|
3726
|
-
throw new Error("Stop current session activity before reloading");
|
|
3727
|
-
await this.runTreeExclusiveOperation([{ sessionId: session.sessionId, session }], "Stop current session activity before reloading", async () => {
|
|
3728
|
-
this.publishActivity(session, "
|
|
3747
|
+
throw new Error(this.t("session.stopBeforeReload", "Stop current session activity before reloading"));
|
|
3748
|
+
await this.runTreeExclusiveOperation([{ sessionId: session.sessionId, session }], this.t("session.stopBeforeReload", "Stop current session activity before reloading"), async () => {
|
|
3749
|
+
this.publishActivity(session, this.t("activity.reloading", "Reloading resources"), "active");
|
|
3729
3750
|
const priorGeneration = this.notificationGenerationBySession.get(session);
|
|
3730
3751
|
let candidateGeneration;
|
|
3731
3752
|
try {
|
|
@@ -3740,7 +3761,7 @@ export class PiSessionService {
|
|
|
3740
3761
|
if (candidateGeneration !== undefined) {
|
|
3741
3762
|
this.publishNotificationMutations(this.notificationsForSession(session).commitReplacement(candidateGeneration));
|
|
3742
3763
|
}
|
|
3743
|
-
this.publishActivity(session, "
|
|
3764
|
+
this.publishActivity(session, this.t("activity.reloaded", "Resources reloaded"), "idle");
|
|
3744
3765
|
this.publishStatus(session);
|
|
3745
3766
|
}
|
|
3746
3767
|
catch (error) {
|
|
@@ -3749,7 +3770,7 @@ export class PiSessionService {
|
|
|
3749
3770
|
this.notificationGenerationBySession.set(session, candidateGeneration);
|
|
3750
3771
|
}
|
|
3751
3772
|
const message = error instanceof Error ? error.message : String(error);
|
|
3752
|
-
this.publishActivity(session, "
|
|
3773
|
+
this.publishActivity(session, this.t("activity.reloadFailed", "Reload failed"), "error", message);
|
|
3753
3774
|
this.events.publish(session.sessionId, { type: "session.error", message }, this.eventScopeForSession(session));
|
|
3754
3775
|
this.publishStatus(session);
|
|
3755
3776
|
throw error;
|
|
@@ -3769,7 +3790,7 @@ export class PiSessionService {
|
|
|
3769
3790
|
return [...this.active.values()].some((active) => active.runtime.session === session);
|
|
3770
3791
|
}
|
|
3771
3792
|
async createSessionRuntime(sessionManager, cwd, options, startup) {
|
|
3772
|
-
startup.report(
|
|
3793
|
+
startup.report(this.t("startup.runtime", "Loading session runtime"));
|
|
3773
3794
|
options.startupSignal?.throwIfAborted();
|
|
3774
3795
|
// Ordinary local sessions (undefined provenance) keep the current rule.
|
|
3775
3796
|
// Only an explicit host one-shot forces delegation off; do not treat every
|
|
@@ -3836,7 +3857,7 @@ export class PiSessionService {
|
|
|
3836
3857
|
await this.recoverSubsessionTrackingForOpenedSession(runtime.session);
|
|
3837
3858
|
}
|
|
3838
3859
|
startupSignal?.throwIfAborted();
|
|
3839
|
-
startup.report(
|
|
3860
|
+
startup.report(this.t("startup.extensions", "Starting session extensions"));
|
|
3840
3861
|
// Startup hooks emit through the same scoped stream as hosted turns.
|
|
3841
3862
|
this.bindRuntime(active);
|
|
3842
3863
|
await this.bindSessionExtensions(runtime.session, notificationGeneration, active, startupSignal);
|
|
@@ -3992,7 +4013,7 @@ export class PiSessionService {
|
|
|
3992
4013
|
await this.publishUnreadMutations(mutations);
|
|
3993
4014
|
}
|
|
3994
4015
|
observeUnreadActivityState(session) {
|
|
3995
|
-
const mutations = this.unreadForScope(this.eventScopeForSession(session)).observeActivityState(session.sessionId, canonicalizeStoredCwd(session.sessionManager.getCwd()), this.hasActiveWork(session));
|
|
4016
|
+
const mutations = this.unreadForScope(this.eventScopeForSession(session)).observeActivityState(session.sessionId, canonicalizeStoredCwd(session.sessionManager.getCwd()), this.hasActiveWork(session), () => ({ messageCount: historyMessages(session).length, leafId: session.sessionManager.getLeafId() }));
|
|
3996
4017
|
if (mutations.length === 0)
|
|
3997
4018
|
return;
|
|
3998
4019
|
void this.publishUnreadMutations(mutations).catch(() => undefined);
|
|
@@ -4143,8 +4164,8 @@ function findSessionByIdOrPrefix(sessions, sessionId) {
|
|
|
4143
4164
|
function uniqueStrings(values) {
|
|
4144
4165
|
return [...new Set(values)];
|
|
4145
4166
|
}
|
|
4146
|
-
function errorMessage(error) {
|
|
4147
|
-
return error
|
|
4167
|
+
function errorMessage(error, t = createTranslator("en", {})) {
|
|
4168
|
+
return userFacingErrorMessage(error, t);
|
|
4148
4169
|
}
|
|
4149
4170
|
function modelToClientModel(model) {
|
|
4150
4171
|
if (model === undefined)
|