aws-runtime-bridge 1.9.0 → 1.9.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/adapter/AcodeSdkAdapter.d.ts +2 -0
- package/dist/adapter/AcodeSdkAdapter.d.ts.map +1 -1
- package/dist/adapter/AcodeSdkAdapter.js +84 -18
- package/dist/adapter/AcodeSdkAdapter.test.js +275 -13
- package/dist/adapter/acode-tool-names.d.ts +16 -0
- package/dist/adapter/acode-tool-names.d.ts.map +1 -0
- package/dist/adapter/acode-tool-names.js +37 -0
- package/dist/adapter/acode-tool-names.test.d.ts +2 -0
- package/dist/adapter/acode-tool-names.test.d.ts.map +1 -0
- package/dist/adapter/acode-tool-names.test.js +28 -0
- package/dist/adapter/types.d.ts +22 -3
- package/dist/adapter/types.d.ts.map +1 -1
- package/dist/adapter/types.js +2 -1
- package/dist/routes/git.d.ts +10 -0
- package/dist/routes/git.d.ts.map +1 -1
- package/dist/routes/git.js +37 -3
- package/dist/routes/git.test.js +47 -1
- package/dist/routes/sessions.d.ts +1 -1
- package/dist/routes/sessions.d.ts.map +1 -1
- package/dist/routes/sessions.js +11 -4
- package/dist/routes/sessions.test.js +3 -0
- package/dist/routes/terminal.d.ts +28 -8
- package/dist/routes/terminal.d.ts.map +1 -1
- package/dist/routes/terminal.js +260 -44
- package/dist/routes/terminal.stop-options.test.d.ts +2 -0
- package/dist/routes/terminal.stop-options.test.d.ts.map +1 -0
- package/dist/routes/terminal.stop-options.test.js +39 -0
- package/dist/routes/terminal.test.js +87 -4
- package/dist/services/session-output.d.ts +5 -0
- package/dist/services/session-output.d.ts.map +1 -1
- package/package/acode/dist/built-in-file-tools.d.ts +5 -1
- package/package/acode/dist/built-in-file-tools.d.ts.map +1 -1
- package/package/acode/dist/built-in-file-tools.js +14 -203
- package/package/acode/dist/builtins/commandArtifacts.d.ts +25 -0
- package/package/acode/dist/builtins/commandArtifacts.d.ts.map +1 -0
- package/package/acode/dist/builtins/commandArtifacts.js +77 -0
- package/package/acode/dist/builtins/commandOutputEvents.d.ts +21 -0
- package/package/acode/dist/builtins/commandOutputEvents.d.ts.map +1 -0
- package/package/acode/dist/builtins/commandOutputEvents.js +56 -0
- package/package/acode/dist/builtins/shellCommandPreparation.d.ts +15 -0
- package/package/acode/dist/builtins/shellCommandPreparation.d.ts.map +1 -0
- package/package/acode/dist/builtins/shellCommandPreparation.js +44 -0
- package/package/acode/dist/builtins/shellCommandResult.d.ts +30 -0
- package/package/acode/dist/builtins/shellCommandResult.d.ts.map +1 -0
- package/package/acode/dist/builtins/shellCommandResult.js +71 -0
- package/package/acode/dist/builtins/shellCommandRunner.d.ts +14 -0
- package/package/acode/dist/builtins/shellCommandRunner.d.ts.map +1 -0
- package/package/acode/dist/builtins/shellCommandRunner.js +114 -0
- package/package/acode/dist/builtins/shellOutputDecoder.d.ts +5 -0
- package/package/acode/dist/builtins/shellOutputDecoder.d.ts.map +1 -0
- package/package/acode/dist/builtins/shellOutputDecoder.js +30 -0
- package/package/acode/dist/mcpToolNames.d.ts +11 -0
- package/package/acode/dist/mcpToolNames.d.ts.map +1 -0
- package/package/acode/dist/mcpToolNames.js +17 -0
- package/package/acode/dist/runtime.d.ts +19 -0
- package/package/acode/dist/runtime.d.ts.map +1 -1
- package/package/acode/dist/runtime.js +233 -35
- package/package/acode/dist/types.d.ts +1 -1
- package/package/acode/dist/types.d.ts.map +1 -1
- package/package/acode/package.json +1 -1
- package/package/aws-client-agent-mcp/dist/mcp-server.d.ts +3 -3
- package/package/aws-client-agent-mcp/dist/mcp-server.d.ts.map +1 -1
- package/package/aws-client-agent-mcp/dist/mcp-server.js +10 -7
- package/package/aws-client-agent-mcp/dist/mcp-server.js.map +1 -1
- package/package/aws-client-agent-mcp/dist/mcp-server.test.js +47 -0
- package/package/aws-client-agent-mcp/dist/mcp-server.test.js.map +1 -1
- package/package/aws-client-agent-mcp/dist/mcp-tools.d.ts +1 -36
- package/package/aws-client-agent-mcp/dist/mcp-tools.d.ts.map +1 -1
- package/package/aws-client-agent-mcp/dist/mcp-tools.js +3 -169
- package/package/aws-client-agent-mcp/dist/mcp-tools.js.map +1 -1
- package/package/aws-client-agent-mcp/dist/mcp-tools.test.d.ts +2 -0
- package/package/aws-client-agent-mcp/dist/mcp-tools.test.d.ts.map +1 -0
- package/package/aws-client-agent-mcp/dist/mcp-tools.test.js +45 -0
- package/package/aws-client-agent-mcp/dist/mcp-tools.test.js.map +1 -0
- package/package/aws-client-agent-mcp/dist/memory-tools.d.ts +11 -0
- package/package/aws-client-agent-mcp/dist/memory-tools.d.ts.map +1 -0
- package/package/aws-client-agent-mcp/dist/memory-tools.js +144 -0
- package/package/aws-client-agent-mcp/dist/memory-tools.js.map +1 -0
- package/package/aws-client-agent-mcp/dist/websocket-client.d.ts.map +1 -1
- package/package/aws-client-agent-mcp/dist/websocket-client.js +1 -0
- package/package/aws-client-agent-mcp/dist/websocket-client.js.map +1 -1
- package/package/aws-client-agent-mcp/dist/websocket-client.test.js +43 -3
- package/package/aws-client-agent-mcp/dist/websocket-client.test.js.map +1 -1
- package/package.json +1 -1
package/dist/routes/terminal.js
CHANGED
|
@@ -24,7 +24,53 @@ export const terminalRouter = Router();
|
|
|
24
24
|
export const sdkSessions = new Map();
|
|
25
25
|
const TOOL_RESULT_PREVIEW_MAX_LENGTH = 4000;
|
|
26
26
|
const AI_REQUEST_BODY_MARKER = "\n\n请求体:\n";
|
|
27
|
+
const AI_REQUEST_HEADERS_MARKER = "\n\n请求头:\n";
|
|
27
28
|
const startingAgents = new Set();
|
|
29
|
+
export function resolveRuntimeContextResetSupport(providerId, hasResetContext) {
|
|
30
|
+
if (providerId !== "acode") {
|
|
31
|
+
return {
|
|
32
|
+
supported: false,
|
|
33
|
+
statusCode: 400,
|
|
34
|
+
error: "context reset without restart is only supported by ACode",
|
|
35
|
+
};
|
|
36
|
+
}
|
|
37
|
+
if (!hasResetContext) {
|
|
38
|
+
return {
|
|
39
|
+
supported: false,
|
|
40
|
+
statusCode: 500,
|
|
41
|
+
error: "ACode adapter does not expose resetContext",
|
|
42
|
+
};
|
|
43
|
+
}
|
|
44
|
+
return { supported: true };
|
|
45
|
+
}
|
|
46
|
+
/**
|
|
47
|
+
* Decide whether SDK startup should wait for an external process PID.
|
|
48
|
+
* Main flow: in-process providers return immediately; external CLI providers keep PID probing for orphan cleanup.
|
|
49
|
+
*/
|
|
50
|
+
export function shouldProbeSdkProcessPid(providerId) {
|
|
51
|
+
return providerId !== "acode";
|
|
52
|
+
}
|
|
53
|
+
/**
|
|
54
|
+
* 主流程:解析 runtime stop 请求的状态通知选项。
|
|
55
|
+
* 内部上下文重置可抑制 stopped 广播;普通手动停止默认继续通知调度端。
|
|
56
|
+
*/
|
|
57
|
+
export function parseRuntimeStopOptions(body) {
|
|
58
|
+
const suppressStatus = typeof body === "object" &&
|
|
59
|
+
body !== null &&
|
|
60
|
+
"suppressStatus" in body &&
|
|
61
|
+
body.suppressStatus === true;
|
|
62
|
+
const reason = typeof body === "object" &&
|
|
63
|
+
body !== null &&
|
|
64
|
+
"reason" in body &&
|
|
65
|
+
typeof body.reason === "string" &&
|
|
66
|
+
body.reason.trim()
|
|
67
|
+
? body.reason.trim()
|
|
68
|
+
: undefined;
|
|
69
|
+
return {
|
|
70
|
+
statusNotified: !suppressStatus,
|
|
71
|
+
reason,
|
|
72
|
+
};
|
|
73
|
+
}
|
|
28
74
|
export function resolveStatusChangeUsage(actionInfo) {
|
|
29
75
|
const usage = actionInfo?.usage;
|
|
30
76
|
if (!usage)
|
|
@@ -39,10 +85,24 @@ export function resolveStatusChangeUsage(actionInfo) {
|
|
|
39
85
|
return undefined;
|
|
40
86
|
return { inputTokens, outputTokens };
|
|
41
87
|
}
|
|
88
|
+
export function resolveStatusChangeRuntimeState(status, actionInfo) {
|
|
89
|
+
const normalizedStatus = String(status || "")
|
|
90
|
+
.trim()
|
|
91
|
+
.toLowerCase();
|
|
92
|
+
const normalizedActionType = String(actionInfo?.actionType || "")
|
|
93
|
+
.trim()
|
|
94
|
+
.toLowerCase();
|
|
95
|
+
if (normalizedStatus === "tool_using" && normalizedActionType === "idle") {
|
|
96
|
+
return actionInfo
|
|
97
|
+
? { status: "waiting_input", actionInfo }
|
|
98
|
+
: { status: "waiting_input" };
|
|
99
|
+
}
|
|
100
|
+
return actionInfo ? { status, actionInfo } : { status };
|
|
101
|
+
}
|
|
42
102
|
const terminalCommandStates = new Map();
|
|
43
103
|
const DEFAULT_WINDOWS_TERMINAL_OUTPUT_ENCODING = "gb18030";
|
|
44
104
|
const DEFAULT_TERMINAL_OUTPUT_ENCODING = "utf-8";
|
|
45
|
-
const TERMINAL_COMMAND_OUTPUT_SPILLOVER_LINE_LIMIT =
|
|
105
|
+
const TERMINAL_COMMAND_OUTPUT_SPILLOVER_LINE_LIMIT = 300;
|
|
46
106
|
const TERMINAL_COMMAND_INACTIVITY_TIMEOUT_MS = 60_000;
|
|
47
107
|
function padDatePart(value) {
|
|
48
108
|
return String(value).padStart(2, "0");
|
|
@@ -167,7 +227,7 @@ function findActiveSdkSessionByAgentId(agentId) {
|
|
|
167
227
|
}
|
|
168
228
|
async function buildExistingSessionResponse(entry) {
|
|
169
229
|
const adapter = adapterRegistry.get(entry.providerId || "claude-code");
|
|
170
|
-
const status = resolveSdkSessionDisplayStatus(adapter.getSessionStatus(entry.sessionId), entry.currentRuntimeStatus);
|
|
230
|
+
const status = resolveSdkSessionDisplayStatus(adapter.getSessionStatus(entry.sessionId), entry.currentRuntimeStatus, entry.currentRuntimeActionType);
|
|
171
231
|
return {
|
|
172
232
|
sessionId: entry.sessionId,
|
|
173
233
|
status,
|
|
@@ -187,9 +247,18 @@ async function buildExistingSessionResponse(entry) {
|
|
|
187
247
|
* 解析 SDK 会话对外展示状态。
|
|
188
248
|
* 主流程:优先使用 adapter 当前细粒度状态,其次使用路由内存记录的最近状态,最后才兼容旧会话回退 running。
|
|
189
249
|
*/
|
|
190
|
-
export function resolveSdkSessionDisplayStatus(adapterStatus, currentRuntimeStatus) {
|
|
250
|
+
export function resolveSdkSessionDisplayStatus(adapterStatus, currentRuntimeStatus, currentRuntimeActionType) {
|
|
191
251
|
const normalizedAdapterStatus = String(adapterStatus || "").trim();
|
|
192
252
|
if (normalizedAdapterStatus) {
|
|
253
|
+
const normalizedRuntimeStatus = String(currentRuntimeStatus || "").trim();
|
|
254
|
+
const normalizedActionType = String(currentRuntimeActionType || "")
|
|
255
|
+
.trim()
|
|
256
|
+
.toLowerCase();
|
|
257
|
+
if (normalizedAdapterStatus.toLowerCase() === "tool_using" &&
|
|
258
|
+
normalizedRuntimeStatus.toLowerCase() === "waiting_input" &&
|
|
259
|
+
normalizedActionType === "idle") {
|
|
260
|
+
return normalizedRuntimeStatus;
|
|
261
|
+
}
|
|
193
262
|
return normalizedAdapterStatus;
|
|
194
263
|
}
|
|
195
264
|
const normalizedRuntimeStatus = String(currentRuntimeStatus || "").trim();
|
|
@@ -575,6 +644,9 @@ export function formatSdkOutputEvent(event) {
|
|
|
575
644
|
if (event.type === "thinking") {
|
|
576
645
|
return event.data.text || undefined;
|
|
577
646
|
}
|
|
647
|
+
if (event.type === "command_output") {
|
|
648
|
+
return event.data.text || undefined;
|
|
649
|
+
}
|
|
578
650
|
if (event.type === "error") {
|
|
579
651
|
return event.data.text
|
|
580
652
|
? `\r\n[SDK Error] ${event.data.text}\r\n`
|
|
@@ -587,14 +659,44 @@ export function buildSdkOutputMetadata(event) {
|
|
|
587
659
|
return {
|
|
588
660
|
sourceEventType: event.type,
|
|
589
661
|
logCategory: "acode",
|
|
662
|
+
...buildCommandOutputMetadata(event),
|
|
590
663
|
...(logMessage
|
|
591
664
|
? {
|
|
592
|
-
logLevel: event
|
|
665
|
+
logLevel: resolveSdkLogLevel(event),
|
|
593
666
|
logMessage,
|
|
594
667
|
}
|
|
595
668
|
: {}),
|
|
596
669
|
};
|
|
597
670
|
}
|
|
671
|
+
function resolveSdkLogLevel(event) {
|
|
672
|
+
if (event.type === "error") {
|
|
673
|
+
return "error";
|
|
674
|
+
}
|
|
675
|
+
if (event.type === "command_output" && event.data.stream === "stderr") {
|
|
676
|
+
return "warn";
|
|
677
|
+
}
|
|
678
|
+
return "info";
|
|
679
|
+
}
|
|
680
|
+
function buildCommandOutputMetadata(event) {
|
|
681
|
+
if (event.type !== "command_output") {
|
|
682
|
+
return {};
|
|
683
|
+
}
|
|
684
|
+
return {
|
|
685
|
+
...(typeof event.data.stream === "string"
|
|
686
|
+
? { stream: event.data.stream }
|
|
687
|
+
: {}),
|
|
688
|
+
...(typeof event.data.sequence === "number"
|
|
689
|
+
? { sequence: event.data.sequence }
|
|
690
|
+
: {}),
|
|
691
|
+
...(typeof event.data.cwd === "string" ? { cwd: event.data.cwd } : {}),
|
|
692
|
+
...(typeof event.data.commandFilePath === "string"
|
|
693
|
+
? { commandFilePath: event.data.commandFilePath }
|
|
694
|
+
: {}),
|
|
695
|
+
...(typeof event.data.commandLineCount === "number"
|
|
696
|
+
? { commandLineCount: event.data.commandLineCount }
|
|
697
|
+
: {}),
|
|
698
|
+
};
|
|
699
|
+
}
|
|
598
700
|
function formatSdkLogMessage(event) {
|
|
599
701
|
if (event.type === "ai_request") {
|
|
600
702
|
const model = event.data.model || "unknown-model";
|
|
@@ -610,6 +712,9 @@ function formatSdkLogMessage(event) {
|
|
|
610
712
|
if (event.type === "tool_use_end") {
|
|
611
713
|
return formatSdkToolLogMessage(event, "完成");
|
|
612
714
|
}
|
|
715
|
+
if (event.type === "command_output") {
|
|
716
|
+
return formatCommandOutputLogMessage(event);
|
|
717
|
+
}
|
|
613
718
|
if (event.type === "turn_complete") {
|
|
614
719
|
const finishReason = event.data.finishReason
|
|
615
720
|
? `, finish=${event.data.finishReason}`
|
|
@@ -624,6 +729,15 @@ function formatSdkLogMessage(event) {
|
|
|
624
729
|
}
|
|
625
730
|
return undefined;
|
|
626
731
|
}
|
|
732
|
+
function formatCommandOutputLogMessage(event) {
|
|
733
|
+
const stream = typeof event.data.stream === "string" ? event.data.stream : "mixed";
|
|
734
|
+
const sequence = typeof event.data.sequence === "number" ? ` #${event.data.sequence}` : "";
|
|
735
|
+
const normalized = String(event.data.text || "")
|
|
736
|
+
.replace(/\s+/g, " ")
|
|
737
|
+
.trim();
|
|
738
|
+
const preview = normalized.length > 240 ? `${normalized.slice(0, 240)}...` : normalized;
|
|
739
|
+
return `命令输出(${stream}${sequence}): ${preview || "(empty)"}`;
|
|
740
|
+
}
|
|
627
741
|
function formatSdkToolLogMessage(event, phaseLabel) {
|
|
628
742
|
const actionInfo = getToolActionInfo(event.data.toolName, event.data.toolInput);
|
|
629
743
|
const actionLabel = event.data.actionLabel || actionInfo.actionLabel || "调用工具";
|
|
@@ -694,7 +808,10 @@ function displayStringList(value) {
|
|
|
694
808
|
function formatAiRequestDetail(data) {
|
|
695
809
|
const lines = [
|
|
696
810
|
data.model ? `模型: ${data.model}` : undefined,
|
|
697
|
-
data.endpoint
|
|
811
|
+
data.endpoint || data.requestUrl
|
|
812
|
+
? `接口: ${data.endpoint || data.requestUrl}`
|
|
813
|
+
: undefined,
|
|
814
|
+
data.requestMethod ? `方法: ${data.requestMethod}` : undefined,
|
|
698
815
|
typeof data.stream === "boolean"
|
|
699
816
|
? `流式: ${data.stream ? "是" : "否"}`
|
|
700
817
|
: undefined,
|
|
@@ -708,10 +825,22 @@ function formatAiRequestDetail(data) {
|
|
|
708
825
|
? `最后请求: ${data.lastUserMessagePreview}`
|
|
709
826
|
: undefined,
|
|
710
827
|
].filter((line) => Boolean(line));
|
|
828
|
+
const requestHeaderLines = formatAiRequestHeaders(data.requestHeaders);
|
|
711
829
|
const base = lines.join("\n") || "ACode 正在发送 AI 请求";
|
|
712
|
-
|
|
713
|
-
? `${
|
|
714
|
-
:
|
|
830
|
+
const headerBlock = requestHeaderLines.length > 0
|
|
831
|
+
? `${AI_REQUEST_HEADERS_MARKER}${requestHeaderLines.join("\n")}`
|
|
832
|
+
: "";
|
|
833
|
+
const bodyBlock = data.requestBody
|
|
834
|
+
? `${AI_REQUEST_BODY_MARKER}${data.requestBody}`
|
|
835
|
+
: "";
|
|
836
|
+
return `${base}${headerBlock}${bodyBlock}`;
|
|
837
|
+
}
|
|
838
|
+
function formatAiRequestHeaders(headers) {
|
|
839
|
+
if (!headers)
|
|
840
|
+
return [];
|
|
841
|
+
return Object.entries(headers)
|
|
842
|
+
.map(([name, value]) => `${name}: ${value}`)
|
|
843
|
+
.filter((line) => line.trim().length > 0);
|
|
715
844
|
}
|
|
716
845
|
/**
|
|
717
846
|
* 主干流程:把 provider 事件转换为面板运行状态。
|
|
@@ -719,7 +848,19 @@ function formatAiRequestDetail(data) {
|
|
|
719
848
|
* text_delta 表示助手正在生成;thinking 表示推理/内部过程。
|
|
720
849
|
* tool_use_start 表示正在执行工具;turn_complete 才表示真正空闲。
|
|
721
850
|
*/
|
|
722
|
-
|
|
851
|
+
function isWaitingInputProgressEvent(event, currentRuntimeStatus) {
|
|
852
|
+
const normalizedStatus = String(currentRuntimeStatus || "")
|
|
853
|
+
.trim()
|
|
854
|
+
.toLowerCase();
|
|
855
|
+
if (normalizedStatus !== "waiting_input") {
|
|
856
|
+
return false;
|
|
857
|
+
}
|
|
858
|
+
return event.type === "thinking" || event.type === "text_delta";
|
|
859
|
+
}
|
|
860
|
+
export function resolveRuntimeStatusFromProviderEvent(event, currentRuntimeStatus) {
|
|
861
|
+
if (isWaitingInputProgressEvent(event, currentRuntimeStatus)) {
|
|
862
|
+
return undefined;
|
|
863
|
+
}
|
|
723
864
|
if (event.type === "ai_request") {
|
|
724
865
|
return {
|
|
725
866
|
status: "thinking",
|
|
@@ -812,7 +953,7 @@ function wireSdkAdapterEvents(adapter, definition) {
|
|
|
812
953
|
const entry = sdkSessions.get(event.sessionId);
|
|
813
954
|
if (entry) {
|
|
814
955
|
console.log(`[${definition.displayName} Adapter] Event: ${event.type} for session ${event.sessionId}`);
|
|
815
|
-
const runtimeStatus = resolveRuntimeStatusFromProviderEvent(event);
|
|
956
|
+
const runtimeStatus = resolveRuntimeStatusFromProviderEvent(event, entry.currentRuntimeStatus);
|
|
816
957
|
if (runtimeStatus) {
|
|
817
958
|
void sendStatus(entry.agentId, event.sessionId, runtimeStatus.status, runtimeStatus.actionInfo, resolveStatusChangeUsage(runtimeStatus.actionInfo), {
|
|
818
959
|
runtimeAccessToken: entry.runtimeAccessToken,
|
|
@@ -832,18 +973,20 @@ function wireSdkAdapterEvents(adapter, definition) {
|
|
|
832
973
|
adapter.on("status-change", (sessionId, status, actionInfo) => {
|
|
833
974
|
const entry = sdkSessions.get(sessionId);
|
|
834
975
|
if (entry) {
|
|
835
|
-
|
|
836
|
-
entry.
|
|
837
|
-
entry.
|
|
838
|
-
entry.
|
|
839
|
-
|
|
976
|
+
const runtimeState = resolveStatusChangeRuntimeState(status, actionInfo);
|
|
977
|
+
entry.currentRuntimeStatus = runtimeState.status;
|
|
978
|
+
entry.currentRuntimeActionType = runtimeState.actionInfo?.actionType;
|
|
979
|
+
entry.currentRuntimeActionLabel = runtimeState.actionInfo?.actionLabel;
|
|
980
|
+
entry.currentRuntimeActionDetail =
|
|
981
|
+
runtimeState.actionInfo?.actionDetail;
|
|
982
|
+
console.log(`[${definition.displayName} Adapter] Status changed: ${runtimeState.status} for agent ${entry.agentId}`, runtimeState.actionInfo || "");
|
|
840
983
|
void updatePersistedSessionRuntimeState(sessionId, {
|
|
841
|
-
runtimeStatus: status,
|
|
842
|
-
runtimeActionType: actionInfo?.actionType,
|
|
843
|
-
runtimeActionLabel: actionInfo?.actionLabel,
|
|
844
|
-
runtimeActionDetail: actionInfo?.actionDetail,
|
|
984
|
+
runtimeStatus: runtimeState.status,
|
|
985
|
+
runtimeActionType: runtimeState.actionInfo?.actionType,
|
|
986
|
+
runtimeActionLabel: runtimeState.actionInfo?.actionLabel,
|
|
987
|
+
runtimeActionDetail: runtimeState.actionInfo?.actionDetail,
|
|
845
988
|
});
|
|
846
|
-
void sendStatus(entry.agentId, sessionId, status, actionInfo, resolveStatusChangeUsage(actionInfo), {
|
|
989
|
+
void sendStatus(entry.agentId, sessionId, runtimeState.status, runtimeState.actionInfo, resolveStatusChangeUsage(runtimeState.actionInfo), {
|
|
847
990
|
runtimeAccessToken: entry.runtimeAccessToken,
|
|
848
991
|
schedulerBaseUrl: entry.schedulerBaseUrl,
|
|
849
992
|
});
|
|
@@ -992,29 +1135,31 @@ async function startSdkSession(req, res) {
|
|
|
992
1135
|
// ★ 尝试获取 SDK 进程的 PID(用于孤儿进程检测)
|
|
993
1136
|
// SDK 不直接暴露 PID,通过进程检测服务查找匹配的进程
|
|
994
1137
|
let sdkPid;
|
|
995
|
-
|
|
996
|
-
|
|
997
|
-
|
|
998
|
-
|
|
999
|
-
|
|
1000
|
-
|
|
1001
|
-
|
|
1002
|
-
|
|
1003
|
-
|
|
1004
|
-
|
|
1005
|
-
p.
|
|
1006
|
-
|
|
1007
|
-
|
|
1008
|
-
|
|
1009
|
-
|
|
1138
|
+
if (shouldProbeSdkProcessPid(providerId)) {
|
|
1139
|
+
try {
|
|
1140
|
+
// 等待一小段时间让进程启动
|
|
1141
|
+
await new Promise((resolve) => setTimeout(resolve, 500));
|
|
1142
|
+
// 尝试通过 adapter 获取 PID(如果支持)
|
|
1143
|
+
sdkPid = adapter.getSessionPid?.(sessionId);
|
|
1144
|
+
// 如果 adapter 不支持,尝试通过进程检测服务查找
|
|
1145
|
+
if (!sdkPid) {
|
|
1146
|
+
const processes = findClaudeCodeProcesses();
|
|
1147
|
+
// 查找匹配 agentId 或工作目录的进程
|
|
1148
|
+
const matched = processes.find((p) => p.agentId === agentId ||
|
|
1149
|
+
p.cwd === workspacePath ||
|
|
1150
|
+
p.command.includes(workspacePath));
|
|
1151
|
+
if (matched) {
|
|
1152
|
+
sdkPid = matched.pid;
|
|
1153
|
+
console.log(`[Runtime] Found SDK process PID: ${sdkPid} for agentId=${agentId}`);
|
|
1154
|
+
}
|
|
1010
1155
|
}
|
|
1011
1156
|
}
|
|
1012
|
-
|
|
1013
|
-
|
|
1014
|
-
|
|
1157
|
+
catch (pidError) {
|
|
1158
|
+
console.warn(`[Runtime] Failed to get SDK PID for agentId=${agentId}:`, pidError);
|
|
1159
|
+
}
|
|
1015
1160
|
}
|
|
1016
1161
|
// ★ 持久化会话信息(包含 PID)
|
|
1017
|
-
const startedStatus = resolveSdkSessionDisplayStatus(adapter.getSessionStatus(sessionId), sdkSessions.get(sessionId)?.currentRuntimeStatus);
|
|
1162
|
+
const startedStatus = resolveSdkSessionDisplayStatus(adapter.getSessionStatus(sessionId), sdkSessions.get(sessionId)?.currentRuntimeStatus, sdkSessions.get(sessionId)?.currentRuntimeActionType);
|
|
1018
1163
|
await upsertPersistedSession({
|
|
1019
1164
|
sessionId,
|
|
1020
1165
|
agentId,
|
|
@@ -1369,12 +1514,65 @@ terminalRouter.post("/resize", validateToken, (req, res) => {
|
|
|
1369
1514
|
rows: Number(rows),
|
|
1370
1515
|
});
|
|
1371
1516
|
});
|
|
1517
|
+
terminalRouter.post("/reset-context", validateToken, async (req, res) => {
|
|
1518
|
+
const { sessionId, reason } = req.body || {};
|
|
1519
|
+
if (!sessionId) {
|
|
1520
|
+
res.status(400).json({ error: "sessionId is required" });
|
|
1521
|
+
return;
|
|
1522
|
+
}
|
|
1523
|
+
const sdkEntry = sdkSessions.get(sessionId);
|
|
1524
|
+
if (!sdkEntry) {
|
|
1525
|
+
res.status(404).json({ error: "SDK session not found" });
|
|
1526
|
+
return;
|
|
1527
|
+
}
|
|
1528
|
+
const providerId = sdkEntry.providerId || "claude-code";
|
|
1529
|
+
const adapter = adapterRegistry.get(providerId);
|
|
1530
|
+
const resetContext = adapter.resetContext?.bind(adapter);
|
|
1531
|
+
const support = resolveRuntimeContextResetSupport(providerId, Boolean(resetContext));
|
|
1532
|
+
if (!support.supported) {
|
|
1533
|
+
res.status(support.statusCode).json({ error: support.error });
|
|
1534
|
+
return;
|
|
1535
|
+
}
|
|
1536
|
+
if (!resetContext) {
|
|
1537
|
+
res
|
|
1538
|
+
.status(500)
|
|
1539
|
+
.json({ error: "ACode adapter does not expose resetContext" });
|
|
1540
|
+
return;
|
|
1541
|
+
}
|
|
1542
|
+
try {
|
|
1543
|
+
await resetContext(String(sessionId));
|
|
1544
|
+
const status = resolveSdkSessionDisplayStatus(adapter.getSessionStatus(String(sessionId)), sdkEntry.currentRuntimeStatus, sdkEntry.currentRuntimeActionType);
|
|
1545
|
+
res.json({
|
|
1546
|
+
ok: true,
|
|
1547
|
+
contextReset: true,
|
|
1548
|
+
sessionId,
|
|
1549
|
+
status,
|
|
1550
|
+
runtimeStatus: status,
|
|
1551
|
+
agentId: sdkEntry.agentId,
|
|
1552
|
+
mode: "sdk",
|
|
1553
|
+
providerId,
|
|
1554
|
+
reason: typeof reason === "string" && reason.trim()
|
|
1555
|
+
? reason.trim()
|
|
1556
|
+
: undefined,
|
|
1557
|
+
});
|
|
1558
|
+
}
|
|
1559
|
+
catch (error) {
|
|
1560
|
+
const errorMessage = error instanceof Error ? error.message : String(error);
|
|
1561
|
+
res.status(500).json({
|
|
1562
|
+
ok: false,
|
|
1563
|
+
contextReset: false,
|
|
1564
|
+
mode: "sdk",
|
|
1565
|
+
error: errorMessage,
|
|
1566
|
+
});
|
|
1567
|
+
}
|
|
1568
|
+
});
|
|
1372
1569
|
/**
|
|
1373
1570
|
* 停止 SDK 会话。
|
|
1374
1571
|
* POST /runtime/stop
|
|
1375
1572
|
*/
|
|
1376
1573
|
terminalRouter.post("/stop", validateToken, async (req, res) => {
|
|
1377
1574
|
const { sessionId } = req.body || {};
|
|
1575
|
+
const stopOptions = parseRuntimeStopOptions(req.body);
|
|
1378
1576
|
if (!sessionId) {
|
|
1379
1577
|
res.status(400).json({ error: "sessionId is required" });
|
|
1380
1578
|
return;
|
|
@@ -1382,11 +1580,18 @@ terminalRouter.post("/stop", validateToken, async (req, res) => {
|
|
|
1382
1580
|
const sdkEntry = sdkSessions.get(sessionId);
|
|
1383
1581
|
if (!sdkEntry) {
|
|
1384
1582
|
await removePersistedSession(sessionId);
|
|
1385
|
-
res.json({
|
|
1583
|
+
res.json({
|
|
1584
|
+
ok: true,
|
|
1585
|
+
status: "stopped",
|
|
1586
|
+
mode: "none",
|
|
1587
|
+
statusNotified: false,
|
|
1588
|
+
reason: stopOptions.reason,
|
|
1589
|
+
});
|
|
1386
1590
|
return;
|
|
1387
1591
|
}
|
|
1388
1592
|
const agentId = sdkEntry.agentId;
|
|
1389
1593
|
try {
|
|
1594
|
+
console.log(`[Runtime] Stopping SDK session ${sessionId}; suppressStatus=${!stopOptions.statusNotified}, reason=${stopOptions.reason || "unspecified"}`);
|
|
1390
1595
|
const providerId = sdkEntry.providerId || "claude-code";
|
|
1391
1596
|
const adapter = adapterRegistry.get(providerId);
|
|
1392
1597
|
await adapter.terminateSession(sessionId);
|
|
@@ -1395,11 +1600,22 @@ terminalRouter.post("/stop", validateToken, async (req, res) => {
|
|
|
1395
1600
|
await removePersistedSession(sessionId);
|
|
1396
1601
|
const processManager = getAgentProcessManager();
|
|
1397
1602
|
await processManager.removeProcess(agentId);
|
|
1398
|
-
|
|
1399
|
-
|
|
1400
|
-
|
|
1603
|
+
if (stopOptions.statusNotified) {
|
|
1604
|
+
await sendStatus(agentId, null, "stopped", undefined, undefined, {
|
|
1605
|
+
runtimeAccessToken: sdkEntry.runtimeAccessToken,
|
|
1606
|
+
schedulerBaseUrl: sdkEntry.schedulerBaseUrl,
|
|
1607
|
+
});
|
|
1608
|
+
}
|
|
1609
|
+
else {
|
|
1610
|
+
console.log(`[Runtime] Stopped status notification suppressed for SDK session ${sessionId}; reason=${stopOptions.reason || "unspecified"}`);
|
|
1611
|
+
}
|
|
1612
|
+
res.json({
|
|
1613
|
+
ok: true,
|
|
1614
|
+
status: "stopped",
|
|
1615
|
+
mode: "sdk",
|
|
1616
|
+
statusNotified: stopOptions.statusNotified,
|
|
1617
|
+
reason: stopOptions.reason,
|
|
1401
1618
|
});
|
|
1402
|
-
res.json({ ok: true, status: "stopped", mode: "sdk" });
|
|
1403
1619
|
}
|
|
1404
1620
|
catch (error) {
|
|
1405
1621
|
const errorMessage = error instanceof Error ? error.message : String(error);
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"terminal.stop-options.test.d.ts","sourceRoot":"","sources":["../../src/routes/terminal.stop-options.test.ts"],"names":[],"mappings":""}
|
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
import { describe, expect, it } from "vitest";
|
|
2
|
+
import { parseRuntimeStopOptions, resolveRuntimeContextResetSupport, } from "./terminal.js";
|
|
3
|
+
describe("runtime stop options", () => {
|
|
4
|
+
it("notifies stopped status for manual stop requests by default", () => {
|
|
5
|
+
const options = parseRuntimeStopOptions({ sessionId: "session-1" });
|
|
6
|
+
expect(options).toEqual({
|
|
7
|
+
statusNotified: true,
|
|
8
|
+
reason: undefined,
|
|
9
|
+
});
|
|
10
|
+
});
|
|
11
|
+
it("suppresses stopped status for internal context reset requests", () => {
|
|
12
|
+
const options = parseRuntimeStopOptions({
|
|
13
|
+
sessionId: "session-1",
|
|
14
|
+
suppressStatus: true,
|
|
15
|
+
reason: "context_reset_after_task_submit",
|
|
16
|
+
});
|
|
17
|
+
expect(options).toEqual({
|
|
18
|
+
statusNotified: false,
|
|
19
|
+
reason: "context_reset_after_task_submit",
|
|
20
|
+
});
|
|
21
|
+
});
|
|
22
|
+
});
|
|
23
|
+
describe("runtime context reset support", () => {
|
|
24
|
+
it("allows only ACode sessions whose adapter exposes resetContext", () => {
|
|
25
|
+
expect(resolveRuntimeContextResetSupport("acode", true)).toEqual({
|
|
26
|
+
supported: true,
|
|
27
|
+
});
|
|
28
|
+
expect(resolveRuntimeContextResetSupport("claude-code", false)).toEqual({
|
|
29
|
+
supported: false,
|
|
30
|
+
statusCode: 400,
|
|
31
|
+
error: "context reset without restart is only supported by ACode",
|
|
32
|
+
});
|
|
33
|
+
expect(resolveRuntimeContextResetSupport("acode", false)).toEqual({
|
|
34
|
+
supported: false,
|
|
35
|
+
statusCode: 500,
|
|
36
|
+
error: "ACode adapter does not expose resetContext",
|
|
37
|
+
});
|
|
38
|
+
});
|
|
39
|
+
});
|
|
@@ -3,7 +3,7 @@ import os from "node:os";
|
|
|
3
3
|
import path from "node:path";
|
|
4
4
|
import { describe, expect, it } from "vitest";
|
|
5
5
|
import { SDK_PROVIDER_DEFINITIONS } from "../adapter/SdkProviderSpi.js";
|
|
6
|
-
import { buildAcodeCommandOutputPath, buildClaudeCodeLaunchConfig, buildRuntimeEnv, buildSdkOutputMetadata, buildToolResultTimelineActionInfo, countTerminalOutputLines, createTerminalOutputDecoder, decodeTerminalOutputChunk, evaluatePersistedSessionReuse, formatCommandInactivityTimeoutNotice, formatCommandOutputSpilloverNotice, formatTerminalPrompt, formatSdkOutputEvent, normalizeTerminalCommandInput, parseTerminalDirectoryChangeTarget, resolveSdkInputMessage, resolveTerminalOutputEncoding, resolveSdkProviderId, resolveSdkSessionDisplayStatus, resolveRuntimeStatusFromProviderEvent, resolveStatusChangeUsage, } from "./terminal.js";
|
|
6
|
+
import { buildAcodeCommandOutputPath, buildClaudeCodeLaunchConfig, buildRuntimeEnv, buildSdkOutputMetadata, buildToolResultTimelineActionInfo, countTerminalOutputLines, createTerminalOutputDecoder, decodeTerminalOutputChunk, evaluatePersistedSessionReuse, formatCommandInactivityTimeoutNotice, formatCommandOutputSpilloverNotice, formatTerminalPrompt, formatSdkOutputEvent, normalizeTerminalCommandInput, parseTerminalDirectoryChangeTarget, resolveSdkInputMessage, resolveTerminalOutputEncoding, resolveSdkProviderId, resolveSdkSessionDisplayStatus, resolveRuntimeStatusFromProviderEvent, resolveStatusChangeRuntimeState, resolveStatusChangeUsage, shouldProbeSdkProcessPid, } from "./terminal.js";
|
|
7
7
|
describe("terminal route validation", () => {
|
|
8
8
|
it("requires agentId and workspacePath for start", () => {
|
|
9
9
|
const validateStartRequest = (body) => {
|
|
@@ -90,6 +90,10 @@ describe("terminal route validation", () => {
|
|
|
90
90
|
});
|
|
91
91
|
});
|
|
92
92
|
describe("SDK startup idle command ordering", () => {
|
|
93
|
+
it("skips PID probing for in-process ACode sessions", () => {
|
|
94
|
+
expect(shouldProbeSdkProcessPid("acode")).toBe(false);
|
|
95
|
+
expect(shouldProbeSdkProcessPid("claude-code")).toBe(true);
|
|
96
|
+
});
|
|
93
97
|
it("sets idle commands immediately after adapter startup and before PID probing or persistence", async () => {
|
|
94
98
|
const calls = [];
|
|
95
99
|
const adapter = {
|
|
@@ -217,10 +221,19 @@ describe("SDK provider event runtime status mapping", () => {
|
|
|
217
221
|
expect(resolveRuntimeStatusFromProviderEvent(event("thinking"))?.status).toBe("thinking");
|
|
218
222
|
expect(resolveRuntimeStatusFromProviderEvent(event("text_delta"))?.status).toBe("thinking");
|
|
219
223
|
});
|
|
224
|
+
it("keeps command output chunks from changing the runtime status", () => {
|
|
225
|
+
expect(resolveRuntimeStatusFromProviderEvent(event("command_output", { text: "stdout chunk", stream: "stdout" }))).toBeUndefined();
|
|
226
|
+
});
|
|
220
227
|
it("maps AI request events to thinking status with request details", () => {
|
|
221
228
|
const state = resolveRuntimeStatusFromProviderEvent(event("ai_request", {
|
|
222
229
|
model: "test-model",
|
|
223
230
|
endpoint: "https://example.invalid/v1/chat/completions",
|
|
231
|
+
requestMethod: "POST",
|
|
232
|
+
requestUrl: "https://example.invalid/v1/chat/completions",
|
|
233
|
+
requestHeaders: {
|
|
234
|
+
"Content-Type": "application/json",
|
|
235
|
+
Authorization: "Bearer ***",
|
|
236
|
+
},
|
|
224
237
|
stream: true,
|
|
225
238
|
messageCount: 3,
|
|
226
239
|
toolCount: 2,
|
|
@@ -234,9 +247,25 @@ describe("SDK provider event runtime status mapping", () => {
|
|
|
234
247
|
});
|
|
235
248
|
expect(state?.actionInfo?.actionDetail).toContain("模型: test-model");
|
|
236
249
|
expect(state?.actionInfo?.actionDetail).toContain("接口: https://example.invalid/v1/chat/completions");
|
|
250
|
+
expect(state?.actionInfo?.actionDetail).toContain("方法: POST");
|
|
251
|
+
expect(state?.actionInfo?.actionDetail).toContain("请求头:\nContent-Type: application/json\nAuthorization: Bearer ***");
|
|
237
252
|
expect(state?.actionInfo?.actionDetail).toContain("最后请求: inspect status");
|
|
238
253
|
expect(state?.actionInfo?.actionDetail).toContain('请求体:\n{"model":"test-model"}');
|
|
239
254
|
});
|
|
255
|
+
it("does not let idle polling text chunks override waiting_input", () => {
|
|
256
|
+
expect(resolveRuntimeStatusFromProviderEvent(event("thinking"), "waiting_input")).toBeUndefined();
|
|
257
|
+
expect(resolveRuntimeStatusFromProviderEvent(event("text_delta"), "waiting_input")).toBeUndefined();
|
|
258
|
+
});
|
|
259
|
+
it("still reports AI request details when a request starts from waiting_input", () => {
|
|
260
|
+
expect(resolveRuntimeStatusFromProviderEvent(event("ai_request", {
|
|
261
|
+
model: "test-model",
|
|
262
|
+
requestBody: '{"model":"test-model"}',
|
|
263
|
+
}), "waiting_input")?.actionInfo).toMatchObject({
|
|
264
|
+
actionType: "ai_request",
|
|
265
|
+
actionLabel: "AI请求",
|
|
266
|
+
actionDetail: expect.stringContaining('请求体:\n{"model":"test-model"}'),
|
|
267
|
+
});
|
|
268
|
+
});
|
|
240
269
|
it("maps tool start events to tool_using with action info", () => {
|
|
241
270
|
const state = resolveRuntimeStatusFromProviderEvent(event("tool_use_start", {
|
|
242
271
|
toolName: "mcp__aws__my_task",
|
|
@@ -315,8 +344,8 @@ describe("terminal command helpers", () => {
|
|
|
315
344
|
expect(outputPath).toBe(path.join("D:", "code", "repo", ".agentswork", "acode", "cmd-dist", "2026-02-03", "04", "05", "20260203-040506-007-cmd.out"));
|
|
316
345
|
});
|
|
317
346
|
it("formats ACode long-output and inactivity notices for terminal users", () => {
|
|
318
|
-
expect(formatCommandOutputSpilloverNotice("/repo/out.txt",
|
|
319
|
-
expect(formatCommandOutputSpilloverNotice("/repo/out.txt",
|
|
347
|
+
expect(formatCommandOutputSpilloverNotice("/repo/out.txt", 300)).toContain("命令输出超过 300 行");
|
|
348
|
+
expect(formatCommandOutputSpilloverNotice("/repo/out.txt", 300)).toContain("/repo/out.txt");
|
|
320
349
|
expect(formatCommandInactivityTimeoutNotice(60_000)).toContain("超过 60 秒没有新的输出");
|
|
321
350
|
});
|
|
322
351
|
});
|
|
@@ -343,6 +372,20 @@ describe("resolveSdkProviderId", () => {
|
|
|
343
372
|
});
|
|
344
373
|
});
|
|
345
374
|
describe("SDK status usage forwarding", () => {
|
|
375
|
+
it("normalizes idle tool status changes to waiting_input", () => {
|
|
376
|
+
expect(resolveStatusChangeRuntimeState("tool_using", {
|
|
377
|
+
actionType: "idle",
|
|
378
|
+
actionLabel: "监听消息",
|
|
379
|
+
actionDetail: "aws-mcp/poll_message",
|
|
380
|
+
})).toEqual({
|
|
381
|
+
status: "waiting_input",
|
|
382
|
+
actionInfo: {
|
|
383
|
+
actionType: "idle",
|
|
384
|
+
actionLabel: "监听消息",
|
|
385
|
+
actionDetail: "aws-mcp/poll_message",
|
|
386
|
+
},
|
|
387
|
+
});
|
|
388
|
+
});
|
|
346
389
|
it("keeps accumulated usage from status-change action info", () => {
|
|
347
390
|
expect(resolveStatusChangeUsage({
|
|
348
391
|
usage: { inputTokens: 12000, outputTokens: 345 },
|
|
@@ -435,6 +478,20 @@ describe("SDK output forwarding", () => {
|
|
|
435
478
|
};
|
|
436
479
|
expect(formatSdkOutputEvent(thinkingEvent)).toBe("internal prompt or reasoning");
|
|
437
480
|
});
|
|
481
|
+
it("formats command output provider events as realtime output chunks", () => {
|
|
482
|
+
const commandOutputEvent = {
|
|
483
|
+
type: "command_output",
|
|
484
|
+
sessionId: "session-1",
|
|
485
|
+
timestamp: new Date(0).toISOString(),
|
|
486
|
+
data: {
|
|
487
|
+
text: "stdout chunk",
|
|
488
|
+
stream: "stdout",
|
|
489
|
+
sequence: 7,
|
|
490
|
+
cwd: "/repo",
|
|
491
|
+
},
|
|
492
|
+
};
|
|
493
|
+
expect(formatSdkOutputEvent(commandOutputEvent)).toBe("stdout chunk");
|
|
494
|
+
});
|
|
438
495
|
it("formats SDK errors as terminal output and ignores non-output events", () => {
|
|
439
496
|
const errorEvent = {
|
|
440
497
|
type: "error",
|
|
@@ -468,7 +525,11 @@ describe("SDK output forwarding", () => {
|
|
|
468
525
|
type: "ai_request",
|
|
469
526
|
sessionId: "session-1",
|
|
470
527
|
timestamp: new Date(0).toISOString(),
|
|
471
|
-
data: {
|
|
528
|
+
data: {
|
|
529
|
+
model: "test-model",
|
|
530
|
+
endpoint: "openai-compatible",
|
|
531
|
+
toolCount: 2,
|
|
532
|
+
},
|
|
472
533
|
};
|
|
473
534
|
const errorEvent = {
|
|
474
535
|
type: "error",
|
|
@@ -489,6 +550,28 @@ describe("SDK output forwarding", () => {
|
|
|
489
550
|
logMessage: "provider failed",
|
|
490
551
|
});
|
|
491
552
|
});
|
|
553
|
+
it("builds ordered ACode log metadata for command output callbacks", () => {
|
|
554
|
+
const commandOutputEvent = {
|
|
555
|
+
type: "command_output",
|
|
556
|
+
sessionId: "session-1",
|
|
557
|
+
timestamp: new Date(0).toISOString(),
|
|
558
|
+
data: {
|
|
559
|
+
text: "stdout chunk",
|
|
560
|
+
stream: "stdout",
|
|
561
|
+
sequence: 7,
|
|
562
|
+
cwd: "/repo",
|
|
563
|
+
},
|
|
564
|
+
};
|
|
565
|
+
expect(buildSdkOutputMetadata(commandOutputEvent)).toEqual({
|
|
566
|
+
sourceEventType: "command_output",
|
|
567
|
+
logCategory: "acode",
|
|
568
|
+
logLevel: "info",
|
|
569
|
+
logMessage: "命令输出(stdout #7): stdout chunk",
|
|
570
|
+
stream: "stdout",
|
|
571
|
+
sequence: 7,
|
|
572
|
+
cwd: "/repo",
|
|
573
|
+
});
|
|
574
|
+
});
|
|
492
575
|
it("does not mark streaming text chunks as standalone ACode log rows", () => {
|
|
493
576
|
const metadata = buildSdkOutputMetadata({
|
|
494
577
|
type: "text_delta",
|
|
@@ -21,6 +21,11 @@ export interface RuntimeOutputMetadata {
|
|
|
21
21
|
logLevel?: "info" | "warn" | "error";
|
|
22
22
|
logMessage?: string;
|
|
23
23
|
logCategory?: string;
|
|
24
|
+
stream?: string;
|
|
25
|
+
sequence?: number;
|
|
26
|
+
cwd?: string;
|
|
27
|
+
commandFilePath?: string;
|
|
28
|
+
commandLineCount?: number;
|
|
24
29
|
}
|
|
25
30
|
/** 活跃会话存储 */
|
|
26
31
|
export declare const sessions: Map<string, Session>;
|