feat: add pure ssh2 remote operations

This commit is contained in:
云服务部-叶林立
2026-08-21 19:51:43 +08:00
parent d3bf562189
commit 0ac50eb581
62 changed files with 3701 additions and 47 deletions
+3
View File
@@ -7,6 +7,9 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
## [Unreleased]
### Added
- Treat `ssh_bash` as a Bash output-compaction alias for ANSI stripping, build/test/Git/linter aggregation, and bounded truncation without enabling remote command rewriting; keep `ssh_find`, `ssh_grep`, and `ssh_read` outside RTK processing.
## [0.9.0] - 2026-07-03
### Changed
+2
View File
@@ -40,6 +40,8 @@ Multi-stage pipeline to reduce token consumption:
| Anchor-Safe Read Compaction | Detects hashline/anchored `read` output and preserves complete edit anchors when filtering or truncating anchored lines |
| Hard Truncation | Final character limit enforcement |
Bash output compaction applies to both the local `bash` tool and `pi-ssh`'s `ssh_bash` output alias. This only sanitizes/compacts returned text: RTK never rewrites remote commands. Structured remote search results from `ssh_find` and `ssh_grep`, and exact remote file reads from `ssh_read`, remain outside RTK processing.
### Interactive Settings
- Tabbed TUI settings modal via `/rtk` command
+24
View File
@@ -318,6 +318,10 @@ await runTest("session_start refreshes RTK provenance and runtime guard skips mi
assert.equal(rewriteCalls, 1);
assert.ok((rewrittenEvent.input as { command: string }).command.includes("rtk git status"));
const remoteEvent = { toolName: "ssh_bash", input: { command: "git status" } };
await toolCallHandler(remoteEvent, createNotificationContext(notifications));
assert.equal(remoteEvent.input.command, "git status");
assert.equal(rewriteCalls, 1, "ssh_bash output support must not enable remote command rewriting");
assert.ok(execCommands.includes("/opt/rtk/bin/rtk"));
writeTestConfig(false);
});
@@ -367,6 +371,26 @@ await runTest("tool execution lifecycle sanitizes streamed bash output", async (
};
await endHandler(endEvent, {});
assert.equal(firstText(endEvent.result.content), "error: build failed\n");
await startHandler(
{ toolName: "ssh_bash", toolCallId: "ssh-bash-1", args: { command: "npm test" } },
{},
);
const remoteUpdate = {
toolName: "ssh_bash",
toolCallId: "ssh-bash-1",
args: { command: "npm test" },
partialResult: { content: [{ type: "text", text: "\x1B[32mremote test\x1B[0m\n" }] },
};
await updateHandler(remoteUpdate, {});
assert.equal(firstText(remoteUpdate.partialResult.content), "remote test\n");
const remoteEnd = {
toolName: "ssh_bash",
toolCallId: "ssh-bash-1",
result: { content: [{ type: "text", text: "\x1B[31mremote failed\x1B[0m\n" }] },
};
await endHandler(remoteEnd, {});
assert.equal(firstText(remoteEnd.result.content), "remote failed\n");
});
await runTest("tool_result lifecycle merges compaction metadata with existing details", async () => {
+4 -4
View File
@@ -234,8 +234,8 @@ export default function rtkIntegrationExtension(pi: ExtensionAPI): void {
};
/**
* Shared guard for bash tool-execution events: skips when compaction is
* disabled, normalizes the event to a record, tracks the bash command, and
* Shared guard for local and SSH bash-output execution events: skips when
* compaction is disabled, normalizes the event, tracks the command, and
* returns the record for further handler-specific processing.
*/
const recordBashEventIfEnabled = (
@@ -246,7 +246,7 @@ export default function rtkIntegrationExtension(pi: ExtensionAPI): void {
}
const eventRecord = toRecord(event);
if (eventRecord.toolName !== "bash") {
if (eventRecord.toolName !== "bash" && eventRecord.toolName !== "ssh_bash") {
return null;
}
@@ -409,7 +409,7 @@ export default function rtkIntegrationExtension(pi: ExtensionAPI): void {
pi.on("tool_execution_end", async (event) => {
const eventRecord = toRecord(event);
if (eventRecord.toolName !== "bash") {
if (eventRecord.toolName !== "bash" && eventRecord.toolName !== "ssh_bash") {
return;
}
@@ -70,6 +70,34 @@ function assertNoPartialHashlineAnchors(text: string): void {
}
}
runTest("ssh_bash output uses Bash compaction while remote search output remains untouched", () => {
const config = cloneDefaultConfig();
const sshBash = compactToolResult(
{
toolName: "ssh_bash",
input: { command: "npm test" },
content: [{ type: "text", text: "\x1B[32mTests: 2 passed\x1B[0m\n" }],
},
config,
);
assert.equal(sshBash.changed, true);
assert.ok(sshBash.techniques.includes("ansi"));
assert.equal(firstTextBlock(sshBash.content).includes("\x1B"), false);
for (const toolName of ["ssh_find", "ssh_grep"]) {
const search = compactToolResult(
{
toolName,
input: { pattern: "TODO" },
content: [{ type: "text", text: "\x1B[31mremote result\x1B[0m\n" }],
},
config,
);
assert.equal(search.changed, false);
assert.deepEqual(search.techniques, []);
}
});
runTest("precision read with offset keeps exact output (no source/smart/hard truncation)", () => {
const config = cloneDefaultConfig();
setReadCompaction(config, true);
+1 -1
View File
@@ -627,7 +627,7 @@ export function compactToolResult(
const { changed, mapped: nextContent } = mapTextContentBlocks(sourceContent, (contentBlock) => {
let transformed = { text: contentBlock.text, techniques: [] as string[] };
if (event.toolName === "bash") {
if (event.toolName === "bash" || event.toolName === "ssh_bash") {
transformed = compactBashText(contentBlock.text, normalizeCommand(input), config);
} else if (event.toolName === "read") {
const normalizedPath = normalizePath(input);