Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
15 changes: 12 additions & 3 deletions packages/api/src/services/container-tasks-core.ts
Original file line number Diff line number Diff line change
Expand Up @@ -27,9 +27,18 @@ const commandSuggestsSsh = (command: string): boolean => command.startsWith("ssh
const commandSuggestsAgent = (command: string): boolean =>
interactiveAgentPattern.test(command) || command.includes("docker-git-agent-")

// Rust browser MCP helpers (`browser-connection`, `docker-git-browser-connection`)
// run on an allocated pty, so without this they would be misclassified as visible
// `ssh` terminals and flood the task manager (issue #383).
// CHANGE: classify browser-connection / docker-git-browser-connection helpers as system
// WHY: these Rust MCP helpers run on allocated ptys (pts/N); without this guard
// hasInteractiveTty classifies each as a visible "ssh" terminal and floods the
// task manager with duplicate rows (issue #383)
// QUOTE(ТЗ): n/a
// REF: https://github.com/ProverCoderAI/docker-git/issues/383
// SOURCE: n/a
// FORMAT THEOREM: ∀ cmd ∈ Commands: commandSuggestsBrowserHelper(cmd) → classifyProcess(cmd) = "system"
// PURITY: CORE
// INVARIANT: browserConnectionPattern matches the binary name at start or after '/'
// with a word boundary — covers bare invocations and full absolute paths
// COMPLEXITY: O(1) time / O(1) space — single constant regex test
const browserConnectionPattern = /(?:^|\/)(?:docker-git-browser-connection|browser-connection)\b/u

const commandSuggestsBrowserHelper = (command: string): boolean => browserConnectionPattern.test(command)
Expand Down
37 changes: 37 additions & 0 deletions packages/api/tests/container-tasks-core.test.ts
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
import fc from "fast-check"
import { describe, expect, it } from "vitest"

import { buildContainerTasks } from "../src/services/container-tasks-core.js"
Expand Down Expand Up @@ -55,6 +56,42 @@ describe("container task classification", () => {
expect(withSystem.map((task) => [task.pid, task.kind])).toEqual([[11502, "system"]])
})

it("hides any browser-helper command variant regardless of path prefix or arguments", () => {
const optArgs = fc.option(fc.string({ maxLength: 40 }), { nil: undefined })

const browserCommandArbitrary = fc.oneof(
optArgs.map((args) => (args === undefined ? "browser-connection" : `browser-connection ${args}`)),
optArgs.map((args) =>
args === undefined ? "docker-git-browser-connection" : `docker-git-browser-connection ${args}`
),
optArgs.map((args) =>
args === undefined ? "/usr/local/bin/browser-connection" : `/usr/local/bin/browser-connection ${args}`
),
optArgs.map((args) =>
args === undefined
? "/usr/local/bin/docker-git-browser-connection"
: `/usr/local/bin/docker-git-browser-connection ${args}`
)
)

fc.assert(
fc.property(browserCommandArbitrary, fc.integer({ min: 1, max: 99999 }), (command, pid) => {
const hidden = buildContainerTasks(
[processOf({ command, pid, ppid: 0, tty: "pts/1" })],
[],
false
)
const shown = buildContainerTasks(
[processOf({ command, pid, ppid: 0, tty: "pts/1" })],
[],
true
)
return hidden.length === 0 && shown.every((t) => t.kind === "system")
})
)
})


it("marks descendants of managed agent pid as agent tasks", () => {
const tasks = buildContainerTasks(
[
Expand Down
Loading