@chainingintention/pi-web-cn 1.202606.12 → 1.202606.14
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +2 -2
- package/dist/client/assets/{CodeViewer-BKljKDuK.js → CodeViewer-D5OA_6r4.js} +1 -1
- package/dist/client/assets/{TerminalPanel-DeDTQWls.js → TerminalPanel-kfPHfhUe.js} +9 -9
- package/dist/client/assets/{index-BuVYTYo8.js → index-MmspRbMF.js} +358 -270
- package/dist/client/index.html +1 -1
- package/dist/config.js +26 -0
- package/dist/config.js.map +1 -1
- package/dist/pi-web-plugins/updates/pi-web-plugin.js +58 -1
- package/dist/pi-web-plugins/updates/updatesLogic.js +13 -13
- package/dist/pi-web-plugins/workspace-tasks/tasksPanelElement.js +1 -5
- package/dist/server/configRoutes.js +10 -0
- package/dist/server/configRoutes.js.map +1 -1
- package/dist/server/managementEmbed.js +6 -1
- package/dist/server/managementEmbed.js.map +1 -1
- package/dist/server/sessiond.js +15 -2
- package/dist/server/sessiond.js.map +1 -1
- package/dist/server/sessions/attachmentService.js +2 -2
- package/dist/server/sessions/attachmentService.js.map +1 -1
- package/dist/server/sessions/builtinCommands.js +21 -21
- package/dist/server/sessions/builtinCommands.js.map +1 -1
- package/dist/server/sessions/oauthLoginFlowService.js +15 -15
- package/dist/server/sessions/oauthLoginFlowService.js.map +1 -1
- package/dist/server/sessions/piSessionService.js +71 -33
- package/dist/server/sessions/piSessionService.js.map +1 -1
- package/dist/server/sessions/sessionCommandService.js +32 -28
- package/dist/server/sessions/sessionCommandService.js.map +1 -1
- package/dist/server/sessions/sessionRoutes.js +2 -2
- package/dist/server/sessions/sessionRoutes.js.map +1 -1
- package/dist/server/sessions/spawnSessionTool.js +36 -0
- package/dist/server/sessions/spawnSessionTool.js.map +1 -0
- package/dist/server/sessions/spawnTargetResolver.js +36 -0
- package/dist/server/sessions/spawnTargetResolver.js.map +1 -0
- package/dist/shared/apiTypes.d.ts +7 -1
- package/docs/plugins.md +3 -3
- package/package.json +2 -1
|
@@ -1,25 +1,25 @@
|
|
|
1
1
|
export const BUILTIN_COMMANDS = [
|
|
2
|
-
{ name: "settings", description: "
|
|
3
|
-
{ name: "model", description: "
|
|
4
|
-
{ name: "scoped-models", description: "
|
|
5
|
-
{ name: "export", description: "
|
|
6
|
-
{ name: "import", description: "
|
|
7
|
-
{ name: "share", description: "
|
|
8
|
-
{ name: "copy", description: "
|
|
9
|
-
{ name: "name", description: "
|
|
10
|
-
{ name: "session", description: "
|
|
11
|
-
{ name: "changelog", description: "
|
|
12
|
-
{ name: "hotkeys", description: "
|
|
13
|
-
{ name: "fork", description: "
|
|
14
|
-
{ name: "clone", description: "
|
|
15
|
-
{ name: "tree", description: "
|
|
16
|
-
{ name: "login", description: "
|
|
17
|
-
{ name: "logout", description: "
|
|
18
|
-
{ name: "new", description: "
|
|
19
|
-
{ name: "compact", description: "
|
|
20
|
-
{ name: "resume", description: "
|
|
21
|
-
{ name: "reload", description: "
|
|
22
|
-
{ name: "quit", description: "
|
|
2
|
+
{ name: "settings", description: "打开设置菜单", source: "builtin" },
|
|
3
|
+
{ name: "model", description: "选择模型", source: "builtin" },
|
|
4
|
+
{ name: "scoped-models", description: "启用/禁用可循环切换的模型", source: "builtin" },
|
|
5
|
+
{ name: "export", description: "导出会话", source: "builtin" },
|
|
6
|
+
{ name: "import", description: "从 JSONL 导入并恢复会话", source: "builtin" },
|
|
7
|
+
{ name: "share", description: "将会话分享到私密 GitHub gist", source: "builtin" },
|
|
8
|
+
{ name: "copy", description: "复制上一条代理消息", source: "builtin" },
|
|
9
|
+
{ name: "name", description: "设置会话显示名称", source: "builtin" },
|
|
10
|
+
{ name: "session", description: "显示会话信息和统计", source: "builtin" },
|
|
11
|
+
{ name: "changelog", description: "显示更新日志条目", source: "builtin" },
|
|
12
|
+
{ name: "hotkeys", description: "显示键盘快捷键", source: "builtin" },
|
|
13
|
+
{ name: "fork", description: "从之前的用户消息创建新分叉", source: "builtin" },
|
|
14
|
+
{ name: "clone", description: "在当前位置复制当前会话", source: "builtin" },
|
|
15
|
+
{ name: "tree", description: "导航会话树", source: "builtin" },
|
|
16
|
+
{ name: "login", description: "配置提供商认证", source: "builtin" },
|
|
17
|
+
{ name: "logout", description: "移除提供商认证", source: "builtin" },
|
|
18
|
+
{ name: "new", description: "启动新会话", source: "builtin" },
|
|
19
|
+
{ name: "compact", description: "手动压缩会话上下文", source: "builtin" },
|
|
20
|
+
{ name: "resume", description: "恢复另一个会话", source: "builtin" },
|
|
21
|
+
{ name: "reload", description: "重新加载键位、扩展、技能、提示词和主题", source: "builtin" },
|
|
22
|
+
{ name: "quit", description: "退出 pi", source: "builtin" },
|
|
23
23
|
];
|
|
24
24
|
export function isBuiltinCommand(name) {
|
|
25
25
|
return BUILTIN_COMMANDS.some((command) => command.name === name);
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"builtinCommands.js","sourceRoot":"","sources":["../../../src/server/sessions/builtinCommands.ts"],"names":[],"mappings":"AAEA,MAAM,CAAC,MAAM,gBAAgB,GAAoB;IAC/C,EAAE,IAAI,EAAE,UAAU,EAAE,WAAW,EAAE,
|
|
1
|
+
{"version":3,"file":"builtinCommands.js","sourceRoot":"","sources":["../../../src/server/sessions/builtinCommands.ts"],"names":[],"mappings":"AAEA,MAAM,CAAC,MAAM,gBAAgB,GAAoB;IAC/C,EAAE,IAAI,EAAE,UAAU,EAAE,WAAW,EAAE,QAAQ,EAAE,MAAM,EAAE,SAAS,EAAE;IAC9D,EAAE,IAAI,EAAE,OAAO,EAAE,WAAW,EAAE,MAAM,EAAE,MAAM,EAAE,SAAS,EAAE;IACzD,EAAE,IAAI,EAAE,eAAe,EAAE,WAAW,EAAE,eAAe,EAAE,MAAM,EAAE,SAAS,EAAE;IAC1E,EAAE,IAAI,EAAE,QAAQ,EAAE,WAAW,EAAE,MAAM,EAAE,MAAM,EAAE,SAAS,EAAE;IAC1D,EAAE,IAAI,EAAE,QAAQ,EAAE,WAAW,EAAE,iBAAiB,EAAE,MAAM,EAAE,SAAS,EAAE;IACrE,EAAE,IAAI,EAAE,OAAO,EAAE,WAAW,EAAE,sBAAsB,EAAE,MAAM,EAAE,SAAS,EAAE;IACzE,EAAE,IAAI,EAAE,MAAM,EAAE,WAAW,EAAE,WAAW,EAAE,MAAM,EAAE,SAAS,EAAE;IAC7D,EAAE,IAAI,EAAE,MAAM,EAAE,WAAW,EAAE,UAAU,EAAE,MAAM,EAAE,SAAS,EAAE;IAC5D,EAAE,IAAI,EAAE,SAAS,EAAE,WAAW,EAAE,WAAW,EAAE,MAAM,EAAE,SAAS,EAAE;IAChE,EAAE,IAAI,EAAE,WAAW,EAAE,WAAW,EAAE,UAAU,EAAE,MAAM,EAAE,SAAS,EAAE;IACjE,EAAE,IAAI,EAAE,SAAS,EAAE,WAAW,EAAE,SAAS,EAAE,MAAM,EAAE,SAAS,EAAE;IAC9D,EAAE,IAAI,EAAE,MAAM,EAAE,WAAW,EAAE,eAAe,EAAE,MAAM,EAAE,SAAS,EAAE;IACjE,EAAE,IAAI,EAAE,OAAO,EAAE,WAAW,EAAE,aAAa,EAAE,MAAM,EAAE,SAAS,EAAE;IAChE,EAAE,IAAI,EAAE,MAAM,EAAE,WAAW,EAAE,OAAO,EAAE,MAAM,EAAE,SAAS,EAAE;IACzD,EAAE,IAAI,EAAE,OAAO,EAAE,WAAW,EAAE,SAAS,EAAE,MAAM,EAAE,SAAS,EAAE;IAC5D,EAAE,IAAI,EAAE,QAAQ,EAAE,WAAW,EAAE,SAAS,EAAE,MAAM,EAAE,SAAS,EAAE;IAC7D,EAAE,IAAI,EAAE,KAAK,EAAE,WAAW,EAAE,OAAO,EAAE,MAAM,EAAE,SAAS,EAAE;IACxD,EAAE,IAAI,EAAE,SAAS,EAAE,WAAW,EAAE,WAAW,EAAE,MAAM,EAAE,SAAS,EAAE;IAChE,EAAE,IAAI,EAAE,QAAQ,EAAE,WAAW,EAAE,SAAS,EAAE,MAAM,EAAE,SAAS,EAAE;IAC7D,EAAE,IAAI,EAAE,QAAQ,EAAE,WAAW,EAAE,qBAAqB,EAAE,MAAM,EAAE,SAAS,EAAE;IACzE,EAAE,IAAI,EAAE,MAAM,EAAE,WAAW,EAAE,OAAO,EAAE,MAAM,EAAE,SAAS,EAAE;CAC1D,CAAC;AAEF,MAAM,UAAU,gBAAgB,CAAC,IAAY;IAC3C,OAAO,gBAAgB,CAAC,IAAI,CAAC,CAAC,OAAO,EAAE,EAAE,CAAC,OAAO,CAAC,IAAI,KAAK,IAAI,CAAC,CAAC;AACnE,CAAC"}
|
|
@@ -37,10 +37,10 @@ export class OAuthLoginFlowService {
|
|
|
37
37
|
onDeviceCode: (info) => {
|
|
38
38
|
if (!this.isCurrentRunning(record))
|
|
39
39
|
return;
|
|
40
|
-
this.updateState(record, { ...record.state, auth: { url: info.verificationUri, instructions:
|
|
40
|
+
this.updateState(record, { ...record.state, auth: { url: info.verificationUri, instructions: `输入代码:${info.userCode}` } });
|
|
41
41
|
},
|
|
42
42
|
onPrompt: (prompt) => this.waitForPrompt(record, prompt, "prompt"),
|
|
43
|
-
onManualCodeInput: () => this.waitForPrompt(record, { message: "
|
|
43
|
+
onManualCodeInput: () => this.waitForPrompt(record, { message: "粘贴回调 URL 或授权码", allowEmpty: false }, "manual"),
|
|
44
44
|
onSelect: (prompt) => this.waitForSelect(record, prompt),
|
|
45
45
|
onProgress: (message) => {
|
|
46
46
|
if (!this.isCurrentRunning(record))
|
|
@@ -53,7 +53,7 @@ export class OAuthLoginFlowService {
|
|
|
53
53
|
if (!this.isCurrentRunning(record))
|
|
54
54
|
return;
|
|
55
55
|
record.pending = undefined;
|
|
56
|
-
this.markTerminal(record, { ...withoutInteraction(record.state), status: "complete", progress: [...record.state.progress, "
|
|
56
|
+
this.markTerminal(record, { ...withoutInteraction(record.state), status: "complete", progress: [...record.state.progress, "登录完成"] });
|
|
57
57
|
options.onComplete?.();
|
|
58
58
|
})
|
|
59
59
|
.catch((error) => {
|
|
@@ -69,20 +69,20 @@ export class OAuthLoginFlowService {
|
|
|
69
69
|
get(flowId) {
|
|
70
70
|
const record = this.flows.get(flowId);
|
|
71
71
|
if (record === undefined)
|
|
72
|
-
throw new Error("OAuth
|
|
72
|
+
throw new Error("未找到 OAuth 登录流程");
|
|
73
73
|
return cloneState(record.state);
|
|
74
74
|
}
|
|
75
75
|
respond(flowId, requestId, value) {
|
|
76
76
|
const record = this.flows.get(flowId);
|
|
77
77
|
if (record === undefined)
|
|
78
|
-
throw new Error("OAuth
|
|
78
|
+
throw new Error("未找到 OAuth 登录流程");
|
|
79
79
|
if (record.state.status !== "running")
|
|
80
80
|
return cloneState(record.state);
|
|
81
81
|
const pending = record.pending;
|
|
82
82
|
if (pending?.requestId !== requestId)
|
|
83
|
-
throw new Error("OAuth
|
|
83
|
+
throw new Error("OAuth 登录请求已过期");
|
|
84
84
|
if (!pending.allowEmpty && value.trim() === "")
|
|
85
|
-
throw new Error("
|
|
85
|
+
throw new Error("必须填写一个值");
|
|
86
86
|
record.pending = undefined;
|
|
87
87
|
this.updateState(record, withoutInteraction(record.state));
|
|
88
88
|
pending.resolve(value);
|
|
@@ -91,13 +91,13 @@ export class OAuthLoginFlowService {
|
|
|
91
91
|
cancel(flowId) {
|
|
92
92
|
const record = this.flows.get(flowId);
|
|
93
93
|
if (record === undefined)
|
|
94
|
-
throw new Error("OAuth
|
|
94
|
+
throw new Error("未找到 OAuth 登录流程");
|
|
95
95
|
if (record.state.status === "running") {
|
|
96
96
|
record.abort.abort();
|
|
97
97
|
const pending = record.pending;
|
|
98
98
|
record.pending = undefined;
|
|
99
|
-
this.markTerminal(record, { ...withoutInteraction(record.state), status: "cancelled", error: "
|
|
100
|
-
pending?.reject(new Error("
|
|
99
|
+
this.markTerminal(record, { ...withoutInteraction(record.state), status: "cancelled", error: "登录已取消" });
|
|
100
|
+
pending?.reject(new Error("登录已取消"));
|
|
101
101
|
}
|
|
102
102
|
return cloneState(record.state);
|
|
103
103
|
}
|
|
@@ -107,14 +107,14 @@ export class OAuthLoginFlowService {
|
|
|
107
107
|
record.abort.abort();
|
|
108
108
|
const pending = record.pending;
|
|
109
109
|
record.pending = undefined;
|
|
110
|
-
pending?.reject(new Error("
|
|
110
|
+
pending?.reject(new Error("登录已取消"));
|
|
111
111
|
}
|
|
112
112
|
this.flows.clear();
|
|
113
113
|
}
|
|
114
114
|
waitForPrompt(record, prompt, kind) {
|
|
115
115
|
return new Promise((resolve, reject) => {
|
|
116
116
|
if (!this.isCurrentRunning(record)) {
|
|
117
|
-
reject(new Error("
|
|
117
|
+
reject(new Error("登录已取消"));
|
|
118
118
|
return;
|
|
119
119
|
}
|
|
120
120
|
const requestId = crypto.randomUUID();
|
|
@@ -135,7 +135,7 @@ export class OAuthLoginFlowService {
|
|
|
135
135
|
waitForSelect(record, prompt) {
|
|
136
136
|
return new Promise((resolve, reject) => {
|
|
137
137
|
if (!this.isCurrentRunning(record)) {
|
|
138
|
-
reject(new Error("
|
|
138
|
+
reject(new Error("登录已取消"));
|
|
139
139
|
return;
|
|
140
140
|
}
|
|
141
141
|
const requestId = crypto.randomUUID();
|
|
@@ -188,8 +188,8 @@ export class OAuthLoginFlowService {
|
|
|
188
188
|
record.abort.abort();
|
|
189
189
|
const pending = record.pending;
|
|
190
190
|
record.pending = undefined;
|
|
191
|
-
this.markTerminal(record, { ...withoutInteraction(record.state), status: "error", error: "OAuth
|
|
192
|
-
pending?.reject(new Error("OAuth
|
|
191
|
+
this.markTerminal(record, { ...withoutInteraction(record.state), status: "error", error: "OAuth 登录流程已过期" });
|
|
192
|
+
pending?.reject(new Error("OAuth 登录流程已过期"));
|
|
193
193
|
}
|
|
194
194
|
setTimer(record, delayMs, callback) {
|
|
195
195
|
this.clearTimer(record);
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"oauthLoginFlowService.js","sourceRoot":"","sources":["../../../src/server/sessions/oauthLoginFlowService.ts"],"names":[],"mappings":"AAAA,OAAO,MAAM,MAAM,aAAa,CAAC;AA8BjC,MAAM,uBAAuB,GAAG,CAAC,GAAG,EAAE,GAAG,IAAI,CAAC;AAC9C,MAAM,sBAAsB,GAAG,EAAE,GAAG,EAAE,GAAG,IAAI,CAAC;AAE9C,MAAM,OAAO,qBAAqB;IAMhC,YAAY,UAAwC,EAAE;QALrC,UAAK,GAAG,IAAI,GAAG,EAA2B,CAAC;QAM1D,IAAI,CAAC,aAAa,GAAG,OAAO,CAAC,aAAa,IAAI,uBAAuB,CAAC;QACtE,IAAI,CAAC,YAAY,GAAG,OAAO,CAAC,YAAY,IAAI,sBAAsB,CAAC;QACnE,IAAI,CAAC,GAAG,GAAG,OAAO,CAAC,GAAG,IAAI,CAAC,GAAG,EAAE,CAAC,IAAI,CAAC,GAAG,EAAE,CAAC,CAAC;IAC/C,CAAC;IAED,KAAK,CAAC,OAKL;QACC,MAAM,MAAM,GAAG,MAAM,CAAC,UAAU,EAAE,CAAC;QACnC,MAAM,KAAK,GAAG,IAAI,eAAe,EAAE,CAAC;QACpC,MAAM,MAAM,GAAoB;YAC9B,MAAM;YACN,KAAK;YACL,OAAO,EAAE,SAAS;YAClB,KAAK,EAAE;gBACL,MAAM;gBACN,UAAU,EAAE,OAAO,CAAC,UAAU;gBAC9B,YAAY,EAAE,OAAO,CAAC,YAAY;gBAClC,MAAM,EAAE,SAAS;gBACjB,QAAQ,EAAE,EAAE;aACb;SACF,CAAC;QACF,IAAI,CAAC,KAAK,CAAC,GAAG,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;QAC/B,IAAI,CAAC,qBAAqB,CAAC,MAAM,CAAC,CAAC;QAEnC,MAAM,SAAS,GAAwB;YACrC,MAAM,EAAE,KAAK,CAAC,MAAM;YACpB,MAAM,EAAE,CAAC,IAAI,EAAE,EAAE;gBACf,IAAI,CAAC,IAAI,CAAC,gBAAgB,CAAC,MAAM,CAAC;oBAAE,OAAO;gBAC3C,IAAI,CAAC,WAAW,CAAC,MAAM,EAAE,EAAE,GAAG,MAAM,CAAC,KAAK,EAAE,IAAI,EAAE,IAAI,EAAE,CAAC,CAAC;YAC5D,CAAC;YACD,6EAA6E;YAC7E,2EAA2E;YAC3E,YAAY,EAAE,CAAC,IAAI,EAAE,EAAE;gBACrB,IAAI,CAAC,IAAI,CAAC,gBAAgB,CAAC,MAAM,CAAC;oBAAE,OAAO;gBAC3C,IAAI,CAAC,WAAW,CAAC,MAAM,EAAE,EAAE,GAAG,MAAM,CAAC,KAAK,EAAE,IAAI,EAAE,EAAE,GAAG,EAAE,IAAI,CAAC,eAAe,EAAE,YAAY,EAAE,
|
|
1
|
+
{"version":3,"file":"oauthLoginFlowService.js","sourceRoot":"","sources":["../../../src/server/sessions/oauthLoginFlowService.ts"],"names":[],"mappings":"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"}
|
|
@@ -11,9 +11,16 @@ import { createPiSessionManagerGateway } from "./piSessionManagerGateway.js";
|
|
|
11
11
|
import { attachmentsToInlineImages, saveAttachmentsToWorkspace } from "./attachmentService.js";
|
|
12
12
|
import { parsePromptAttachments } from "../../shared/promptAttachments.js";
|
|
13
13
|
import { cwdPathsEqual } from "../workingDirectory.js";
|
|
14
|
+
import { createSpawnSessionToolDefinition } from "./spawnSessionTool.js";
|
|
15
|
+
const noopLogger = { info() { } };
|
|
14
16
|
function noop() {
|
|
15
17
|
// Intentionally empty default unsubscribe callback.
|
|
16
18
|
}
|
|
19
|
+
function spawnTargetError(decision) {
|
|
20
|
+
if (decision.reason === "not-registered")
|
|
21
|
+
return new Error("派生会话不在已注册项目中");
|
|
22
|
+
return new Error(`cwd 必须是此项目的工作区。允许的路径:${decision.allowedCwds.join(", ")}`);
|
|
23
|
+
}
|
|
17
24
|
function authLossWarningKey(sessionId, provider, modelId) {
|
|
18
25
|
return `${sessionId}:${provider}/${modelId}`;
|
|
19
26
|
}
|
|
@@ -28,7 +35,7 @@ function lookupMatchesActiveSession(ref, active) {
|
|
|
28
35
|
}
|
|
29
36
|
function requirePromptText(value) {
|
|
30
37
|
if (typeof value !== "string")
|
|
31
|
-
throw new Error("
|
|
38
|
+
throw new Error("提示文本为必填项");
|
|
32
39
|
return value;
|
|
33
40
|
}
|
|
34
41
|
function parsePromptStreamingBehavior(value) {
|
|
@@ -43,10 +50,13 @@ function defaultCreateAgentRuntime(createRuntime, options) {
|
|
|
43
50
|
throw new Error("Default runtime creation requires an SDK SessionManager");
|
|
44
51
|
return createAgentSessionRuntime(createRuntime, { ...options, sessionManager: options.sessionManager });
|
|
45
52
|
}
|
|
46
|
-
function createDefaultRuntimeFactory(authStorage, modelRegistry) {
|
|
53
|
+
function createDefaultRuntimeFactory(authStorage, modelRegistry, spawn) {
|
|
47
54
|
return async ({ cwd, agentDir, sessionManager, sessionStartEvent }) => {
|
|
48
55
|
const services = await createAgentSessionServices({ cwd, agentDir, authStorage, modelRegistry });
|
|
49
|
-
const customTools = [
|
|
56
|
+
const customTools = [
|
|
57
|
+
createPiWebEditToolDefinition(cwd),
|
|
58
|
+
...(spawn === undefined ? [] : [createSpawnSessionToolDefinition(cwd, { spawn })]),
|
|
59
|
+
];
|
|
50
60
|
const options = sessionStartEvent === undefined
|
|
51
61
|
? { services, sessionManager, customTools }
|
|
52
62
|
: { services, sessionManager, sessionStartEvent, customTools };
|
|
@@ -87,11 +97,13 @@ export class PiSessionService {
|
|
|
87
97
|
this.agentDir = deps.agentDir ?? getAgentDir();
|
|
88
98
|
this.sessionManager = deps.sessionManager ?? createPiSessionManagerGateway({ agentDir: this.agentDir });
|
|
89
99
|
this.modelRegistry = deps.modelRegistry ?? ModelRegistry.create(AuthStorage.create());
|
|
90
|
-
this.
|
|
100
|
+
this.spawnTargets = deps.spawnTargets;
|
|
101
|
+
this.logger = deps.logger ?? noopLogger;
|
|
102
|
+
this.createRuntime = deps.createRuntime ?? createDefaultRuntimeFactory(this.modelRegistry.authStorage, this.modelRegistry, this.spawnTargets === undefined ? undefined : (input) => this.spawnSession(input));
|
|
91
103
|
this.createAgentRuntime = deps.createAgentRuntime ?? defaultCreateAgentRuntime;
|
|
92
104
|
this.workspaceActivity = deps.workspaceActivity;
|
|
93
105
|
this.heartbeat = setInterval(() => { this.publishHeartbeats(); }, deps.heartbeatIntervalMs ?? 2000);
|
|
94
|
-
this.commandService = new SessionCommandService((sessionId) => this.getActive(sessionId), (sessionId, text) => this.prompt(sessionId, text), events, {
|
|
106
|
+
this.commandService = new SessionCommandService((sessionId) => this.getActive(sessionId), (sessionId, text) => this.prompt(sessionId, text, undefined, undefined, { echoUserMessage: false }), events, {
|
|
95
107
|
onCompactionStart: (session) => {
|
|
96
108
|
this.publishActivity(session, "compacting", "active");
|
|
97
109
|
this.publishStatus(session);
|
|
@@ -139,7 +151,7 @@ export class PiSessionService {
|
|
|
139
151
|
void _managementContext;
|
|
140
152
|
const active = await this.create(this.sessionManager.create(cwd), cwd);
|
|
141
153
|
const { session } = active.runtime;
|
|
142
|
-
|
|
154
|
+
const created = {
|
|
143
155
|
id: session.sessionId,
|
|
144
156
|
path: session.sessionFile ?? "",
|
|
145
157
|
cwd,
|
|
@@ -148,6 +160,26 @@ export class PiSessionService {
|
|
|
148
160
|
messageCount: session.messages.length,
|
|
149
161
|
firstMessage: "",
|
|
150
162
|
};
|
|
163
|
+
// Broadcast so other clients (and the spawning agent's UI) can add the new
|
|
164
|
+
// session to their list without a manual reload.
|
|
165
|
+
this.events.publishGlobal({ type: "session.created", session: created });
|
|
166
|
+
return created;
|
|
167
|
+
}
|
|
168
|
+
/**
|
|
169
|
+
* Start a new session on behalf of a LLM and deliver an initial prompt to it.
|
|
170
|
+
* The target cwd is constrained to a workspace of the same registered project
|
|
171
|
+
* as the spawning session so the new session is visible in the web UI.
|
|
172
|
+
*/
|
|
173
|
+
async spawnSession(input) {
|
|
174
|
+
if (this.spawnTargets === undefined)
|
|
175
|
+
throw new Error("派生会话已禁用");
|
|
176
|
+
const decision = await this.spawnTargets.resolveSpawnTarget(input.spawningCwd, input.cwd);
|
|
177
|
+
if (!decision.allowed)
|
|
178
|
+
throw spawnTargetError(decision);
|
|
179
|
+
const created = await this.start(decision.cwd);
|
|
180
|
+
await this.prompt(created.id, input.prompt);
|
|
181
|
+
this.logger.info({ spawningCwd: input.spawningCwd, sessionId: created.id, cwd: decision.cwd, promptLength: input.prompt.length }, "spawn_session started a new session");
|
|
182
|
+
return { sessionId: created.id, cwd: decision.cwd };
|
|
151
183
|
}
|
|
152
184
|
async messages(ref, page) {
|
|
153
185
|
const session = await this.getOrOpen(ref);
|
|
@@ -174,7 +206,7 @@ export class PiSessionService {
|
|
|
174
206
|
const model = candidates.find((candidate) => candidate.provider === provider && candidate.id === modelId)
|
|
175
207
|
?? session.modelRegistry.find(provider, modelId);
|
|
176
208
|
if (model === undefined)
|
|
177
|
-
throw new Error(
|
|
209
|
+
throw new Error(`未找到模型:${provider}/${modelId}`);
|
|
178
210
|
await session.setModel(model);
|
|
179
211
|
this.publishActivity(session, `model: ${model.id}`, "idle", model.provider);
|
|
180
212
|
this.publishStatus(session);
|
|
@@ -185,7 +217,7 @@ export class PiSessionService {
|
|
|
185
217
|
const session = await this.getOrOpen(ref);
|
|
186
218
|
const result = await session.cycleModel(direction);
|
|
187
219
|
if (result === undefined)
|
|
188
|
-
throw new Error(session.scopedModels.length > 0 ? "
|
|
220
|
+
throw new Error(session.scopedModels.length > 0 ? "作用域内只有一个模型" : "只有一个可用模型");
|
|
189
221
|
this.publishActivity(session, `model: ${result.model.id}`, "idle", result.model.provider);
|
|
190
222
|
this.publishStatus(session);
|
|
191
223
|
return this.statusFromSession(session);
|
|
@@ -202,7 +234,7 @@ export class PiSessionService {
|
|
|
202
234
|
const available = session.getAvailableThinkingLevels();
|
|
203
235
|
const match = available.find((candidate) => candidate === level);
|
|
204
236
|
if (match === undefined)
|
|
205
|
-
throw new Error(
|
|
237
|
+
throw new Error(`无效的思考级别:${level}`);
|
|
206
238
|
session.setThinkingLevel(match);
|
|
207
239
|
this.publishActivity(session, `thinking: ${session.thinkingLevel}`, "idle");
|
|
208
240
|
this.publishStatus(session);
|
|
@@ -213,7 +245,7 @@ export class PiSessionService {
|
|
|
213
245
|
const session = await this.getOrOpen(ref);
|
|
214
246
|
const level = session.cycleThinkingLevel();
|
|
215
247
|
if (level === undefined)
|
|
216
|
-
throw new Error("
|
|
248
|
+
throw new Error("当前模型不支持思考");
|
|
217
249
|
this.publishActivity(session, `thinking: ${level}`, "idle");
|
|
218
250
|
this.publishStatus(session);
|
|
219
251
|
return this.statusFromSession(session);
|
|
@@ -232,9 +264,15 @@ export class PiSessionService {
|
|
|
232
264
|
}
|
|
233
265
|
return commands.sort((a, b) => a.name.localeCompare(b.name));
|
|
234
266
|
}
|
|
235
|
-
async prompt(ref, text, streamingBehavior, attachments,
|
|
267
|
+
async prompt(ref, text, streamingBehavior, attachments, options) {
|
|
268
|
+
const _managementContext = options?.managementContext;
|
|
236
269
|
void _managementContext;
|
|
237
270
|
const promptText = requirePromptText(text);
|
|
271
|
+
// Command-forwarded prompts (e.g. /skill:*) are expanded by the agent, which
|
|
272
|
+
// streams the canonical message back. The client doesn't render the raw
|
|
273
|
+
// command text, so the server must not echo it either, or it would show up
|
|
274
|
+
// as a transient line that vanishes on reload.
|
|
275
|
+
const echoUserMessage = options?.echoUserMessage !== false;
|
|
238
276
|
const requestedBehavior = parsePromptStreamingBehavior(streamingBehavior);
|
|
239
277
|
const parsedAttachments = parsePromptAttachments(attachments, { enforceInlineSizeLimit: false });
|
|
240
278
|
const images = (await attachmentsToInlineImages(parsedAttachments)).map((entry) => entry.image);
|
|
@@ -249,14 +287,14 @@ export class PiSessionService {
|
|
|
249
287
|
return;
|
|
250
288
|
}
|
|
251
289
|
if (session.isCompacting) {
|
|
252
|
-
this.enqueuePromptDuringCompaction(session, promptText, behavior ?? "followUp", images);
|
|
290
|
+
this.enqueuePromptDuringCompaction(session, promptText, behavior ?? "followUp", images, echoUserMessage);
|
|
253
291
|
return;
|
|
254
292
|
}
|
|
255
|
-
void this.submitPrompt(session, promptText, behavior, images);
|
|
293
|
+
void this.submitPrompt(session, promptText, behavior, images, echoUserMessage);
|
|
256
294
|
}
|
|
257
|
-
submitPrompt(session, text, behavior, images = []) {
|
|
295
|
+
submitPrompt(session, text, behavior, images = [], echoUserMessage = true) {
|
|
258
296
|
this.publishActivity(session, behavior === "steer" ? "steering queued" : behavior === "followUp" ? "message queued" : "prompt accepted", "active");
|
|
259
|
-
if (behavior === undefined)
|
|
297
|
+
if (behavior === undefined && echoUserMessage)
|
|
260
298
|
this.events.publish(session.sessionId, { type: "message.append", message: userMessage(text, images) });
|
|
261
299
|
const promptOptions = buildPromptOptions(behavior, images);
|
|
262
300
|
const promptPromise = session.prompt(text, promptOptions).catch((error) => {
|
|
@@ -267,9 +305,9 @@ export class PiSessionService {
|
|
|
267
305
|
void promptPromise;
|
|
268
306
|
return promptPromise;
|
|
269
307
|
}
|
|
270
|
-
enqueuePromptDuringCompaction(session, text, kind, images = []) {
|
|
308
|
+
enqueuePromptDuringCompaction(session, text, kind, images = [], echoUserMessage = true) {
|
|
271
309
|
const queue = this.compactionPromptQueues.get(session.sessionId) ?? [];
|
|
272
|
-
queue.push({ kind, text, ...(images.length > 0 ? { images } : {}) });
|
|
310
|
+
queue.push({ kind, text, ...(images.length > 0 ? { images } : {}), ...(echoUserMessage ? {} : { echoUserMessage: false }) });
|
|
273
311
|
this.compactionPromptQueues.set(session.sessionId, queue);
|
|
274
312
|
this.publishActivity(session, "message queued during compaction", "active");
|
|
275
313
|
this.publishStatus(session);
|
|
@@ -290,9 +328,9 @@ export class PiSessionService {
|
|
|
290
328
|
const isExcluded = text.startsWith("!!");
|
|
291
329
|
const command = (isExcluded ? text.slice(2) : text.slice(1)).trim();
|
|
292
330
|
if (!command)
|
|
293
|
-
throw new Error("
|
|
331
|
+
throw new Error("用法:!<shell command>");
|
|
294
332
|
if (session.isBashRunning)
|
|
295
|
-
throw new Error("
|
|
333
|
+
throw new Error("已有 bash 命令正在运行");
|
|
296
334
|
this.publishActivity(session, "running bash", "active", command);
|
|
297
335
|
this.events.publish(session.sessionId, { type: "shell.start", command, excludeFromContext: isExcluded });
|
|
298
336
|
void session.executeBash(command, (chunk) => {
|
|
@@ -332,7 +370,7 @@ export class PiSessionService {
|
|
|
332
370
|
async archive(ref) {
|
|
333
371
|
const session = await this.getOrOpen(ref);
|
|
334
372
|
if (this.hasActiveWork(session))
|
|
335
|
-
throw new Error("
|
|
373
|
+
throw new Error("归档前请先停止当前会话活动");
|
|
336
374
|
const archiveInput = await this.archiveInputForSession(session);
|
|
337
375
|
await this.closeActive(session.sessionId);
|
|
338
376
|
await this.archiveStore.archive(archiveInput);
|
|
@@ -344,7 +382,7 @@ export class PiSessionService {
|
|
|
344
382
|
const plan = planSessionArchiveTree(root, catalog);
|
|
345
383
|
const busy = plan.targets.map((target) => target.activeSession).find((target) => target !== undefined && this.hasActiveWork(target));
|
|
346
384
|
if (busy !== undefined)
|
|
347
|
-
throw new Error(
|
|
385
|
+
throw new Error(`归档 ${sessionDisplayName(busy)} 前请先停止当前会话活动`);
|
|
348
386
|
const archiveInputs = plan.unarchivedTargets.map((target) => archiveInputFromCandidate(target));
|
|
349
387
|
for (const input of archiveInputs)
|
|
350
388
|
await this.closeActive(input.sessionId);
|
|
@@ -360,16 +398,16 @@ export class PiSessionService {
|
|
|
360
398
|
async restore(ref) {
|
|
361
399
|
const archived = await this.getArchived(ref);
|
|
362
400
|
if (archived === undefined)
|
|
363
|
-
throw new Error("
|
|
401
|
+
throw new Error("未找到会话");
|
|
364
402
|
await this.closeActive(archived.sessionId);
|
|
365
403
|
await this.archiveStore.restore(archived.sessionId);
|
|
366
404
|
}
|
|
367
405
|
async deleteArchived(ref) {
|
|
368
406
|
const record = await this.getArchived(ref);
|
|
369
407
|
if (record === undefined)
|
|
370
|
-
throw new Error("
|
|
408
|
+
throw new Error("未找到已归档会话");
|
|
371
409
|
if (this.archiveStore.deleteArchived === undefined)
|
|
372
|
-
throw new Error("
|
|
410
|
+
throw new Error("归档存储不支持删除");
|
|
373
411
|
await this.closeActive(record.sessionId);
|
|
374
412
|
if (record.archivePath === undefined)
|
|
375
413
|
await this.ensureArchivedRecordMoved(record);
|
|
@@ -379,7 +417,7 @@ export class PiSessionService {
|
|
|
379
417
|
await this.assertWritable(ref);
|
|
380
418
|
const session = await this.getOrOpen(ref);
|
|
381
419
|
if (this.hasActiveWork(session))
|
|
382
|
-
throw new Error("
|
|
420
|
+
throw new Error("重新加载前请先停止当前会话活动");
|
|
383
421
|
await this.closeActive(session.sessionId);
|
|
384
422
|
const reopened = await this.getActive(ref);
|
|
385
423
|
this.publishStatus(reopened.runtime.session);
|
|
@@ -388,7 +426,7 @@ export class PiSessionService {
|
|
|
388
426
|
const session = await this.getOrOpen(ref);
|
|
389
427
|
const sessionFile = session.sessionFile;
|
|
390
428
|
if (sessionFile === undefined || sessionFile === "")
|
|
391
|
-
throw new Error("
|
|
429
|
+
throw new Error("会话尚未持久化");
|
|
392
430
|
await clearParentSession(sessionFile);
|
|
393
431
|
}
|
|
394
432
|
async abort(ref) {
|
|
@@ -439,7 +477,7 @@ export class PiSessionService {
|
|
|
439
477
|
const cwd = session.sessionManager.getCwd();
|
|
440
478
|
const sessionFile = session.sessionFile;
|
|
441
479
|
if (sessionFile === undefined || sessionFile === "")
|
|
442
|
-
throw new Error("
|
|
480
|
+
throw new Error("会话尚未持久化");
|
|
443
481
|
const listed = (await this.sessionManager.list(cwd)).find((candidate) => candidate.id === session.sessionId);
|
|
444
482
|
if (listed !== undefined)
|
|
445
483
|
return archiveInputFromListEntry(listed);
|
|
@@ -515,7 +553,7 @@ export class PiSessionService {
|
|
|
515
553
|
}
|
|
516
554
|
async assertWritable(ref) {
|
|
517
555
|
if (await this.getArchived(ref) !== undefined)
|
|
518
|
-
throw new Error("
|
|
556
|
+
throw new Error("已归档会话为只读。请恢复会话后继续。");
|
|
519
557
|
}
|
|
520
558
|
async getOrOpen(ref) {
|
|
521
559
|
return (await this.getActive(ref)).runtime.session;
|
|
@@ -531,7 +569,7 @@ export class PiSessionService {
|
|
|
531
569
|
? (await this.sessionManager.list(ref.cwd)).find((s) => s.id === ref.id || s.id.startsWith(ref.id))
|
|
532
570
|
: (await this.sessionManager.listAll?.() ?? []).find((s) => s.id === ref || s.id.startsWith(ref));
|
|
533
571
|
if (!match)
|
|
534
|
-
throw new Error("
|
|
572
|
+
throw new Error("未找到会话");
|
|
535
573
|
return this.create(this.sessionManager.open(match.path), match.cwd);
|
|
536
574
|
}
|
|
537
575
|
async getArchived(ref) {
|
|
@@ -621,14 +659,14 @@ export class PiSessionService {
|
|
|
621
659
|
return;
|
|
622
660
|
this.publishStatus(session);
|
|
623
661
|
for (const prompt of queued)
|
|
624
|
-
void this.submitPrompt(session, prompt.text, prompt.kind, prompt.images);
|
|
662
|
+
void this.submitPrompt(session, prompt.text, prompt.kind, prompt.images, prompt.echoUserMessage ?? true);
|
|
625
663
|
return;
|
|
626
664
|
}
|
|
627
665
|
const prompt = this.shiftCompactionPrompt(sessionId);
|
|
628
666
|
if (prompt === undefined)
|
|
629
667
|
return;
|
|
630
668
|
this.publishStatus(session);
|
|
631
|
-
const submitted = this.submitPrompt(session, prompt.text, undefined, prompt.images);
|
|
669
|
+
const submitted = this.submitPrompt(session, prompt.text, undefined, prompt.images, prompt.echoUserMessage ?? true);
|
|
632
670
|
void submitted.finally(() => { this.scheduleCompactionQueueDrain(sessionId); });
|
|
633
671
|
}
|
|
634
672
|
takeCompactionPromptQueue(sessionId) {
|
|
@@ -900,7 +938,7 @@ function archiveInputFromListEntry(session) {
|
|
|
900
938
|
function archiveInputFromActiveSession(session) {
|
|
901
939
|
const sessionFile = session.sessionFile;
|
|
902
940
|
if (sessionFile === undefined || sessionFile === "")
|
|
903
|
-
throw new Error("
|
|
941
|
+
throw new Error("会话尚未持久化");
|
|
904
942
|
const parentSessionPath = session.sessionManager.getHeader?.()?.parentSession;
|
|
905
943
|
return {
|
|
906
944
|
sessionId: session.sessionId,
|
|
@@ -941,7 +979,7 @@ function archiveCandidateFromArchivedRecord(record, fallback) {
|
|
|
941
979
|
function archiveCandidateFromActiveSession(session, archived) {
|
|
942
980
|
const sessionFile = session.sessionFile;
|
|
943
981
|
if (sessionFile === undefined || sessionFile === "")
|
|
944
|
-
throw new Error("
|
|
982
|
+
throw new Error("会话尚未持久化");
|
|
945
983
|
const parentSessionPath = session.sessionManager.getHeader?.()?.parentSession;
|
|
946
984
|
return {
|
|
947
985
|
id: session.sessionId,
|