agentscope-ai / agentscope-ai/agentscope-java

Harness: public shell execution lifecycle provider (stream, cancel, cleanup, readback)

未关闭
#2,138 2 条评论 0 个 reaction 已指派 0 人 在 GitHub 查看
area/harness enhancement
主要语言
Java
星标
5.6k
派生
1.3k
平均合并
4 天 12 小时
30 天内合并 PR
77

描述

## Motivation

AgentScope Harness currently exposes shell execution only as:

```java
ExecuteResponse execute(RuntimeContext context, String command, Integer timeoutSeconds)
```

This synchronous aggregate contract cannot represent execution identity, ordered stdout/stderr chunks, explicit cancellation, background status, process/container cleanup, or restart readback. `AsyncToolMiddleware` is not an execution lifecycle substitute: it offloads an arbitrary tool invocation and explicitly lets the underlying execution continue (`never cancelled`). `TaskRepository` is scoped to subagent tasks and should not become a shell process registry.

The immediate pipe deadlock remains tracked separately in #1519. This proposal is for the product-grade lifecycle after/alongside that focused fix.

Observed against `e3a412ed2cc944e401da861c8d5e464b967724e9`.

## Proposed public contract

Introduce a Harness-owned execution provider SPI, conceptually:

```java
interface ExecutionProvider {
ExecutionHandle start(RuntimeContext context, ExecutionRequest request);
Optional get(RuntimeContext context, String executionId);
ExecutionSnapshot cancel(RuntimeContext context, String executionId);
Flux stream(RuntimeContext context, String executionId, long afterSequence);
}
```

The exact API can differ, but the behavior should include:

- stable `executionId` scoped by tenant/user/session/workspace;
- monotonically sequenced stdout/stderr chunks with bounded retention/truncation facts;
- typed states such as `STARTING/RUNNING/COMPLETED/FAILED/TIMED_OUT/CANCELLED`;
- exactly one terminal transition;
- idempotent cancel;
- direct process plus descendant process-tree cleanup for local execution, and equivalent container/job cleanup for sandbox providers;
- explicit cwd/environment/shell identity derived from the Harness workspace/filesystem policy;
- reconnect/readback after subscriber loss, and a declared capability when restart recovery is unsupported;
- permission/HITL remains before `start`, with no alternate execution path bypassing `PermissionEngine`;
- typed events suitable for `streamEvents()`/OTel without requiring a product host process table.

`ShellExecuteTool` could keep the current aggregate response as a compatibility adapter over the provider, while a lifecycle-aware tool/API uses the typed handle directly.

## Provider ownership and possible implementations

The SPI and lifecycle state should be owned by Harness/sandbox implementations, not by each product host.

- Local provider: JDK `ProcessBuilder` + concurrent bounded drains + `ProcessHandle.descendants()`; Apache Commons Exec could help watchdog/stream pumping but does not define durable identity/readback.
- Interactive optional provider: pty4j/ConPTY, advertised as a separate PTY capability rather than required for non-interactive commands.
- Docker/Kubernetes/cloud sandbox providers: map the same handle/events to container exec/job primitives and their native cleanup/status APIs.

A third-party library can implement a provider, but directly adding it to an application host would create a second shell/process runtime and produce different behavior from Docker/Kubernetes backends.

## Acceptance cases

1. 30K/90K stdout, stderr-only and mixed output complete without pipe deadlock and with bounded retention.
2. Slow streaming output is ordered and resumable from a sequence cursor.
3. Timeout and cancel terminate descendants and produce one terminal state.
4. Cancel is idempotent and races safely with natural completion.
5. Disconnect/reconnect reads the same execution state; restart support is either proven or fail-closed by capability.
6. USER/SESSION workspace namespace and cwd isolation are preserved.
7. Local and sandbox implementations expose the same lifecycle semantics.
8. A product host needs no `ProcessBuilder`, process map, polling scheduler, or shell result store.

## Relationship to existing components

- #1519 should remain a small concurrent-drain bug fix and need not wait for this SPI.
- `AsyncToolMiddleware` can observe/offload a lifecycle-aware tool, but should not own or hide cancellation.
- `TaskRepository` remains the subagent task owner unless Harness intentionally introduces a common, typed job substrate with distinct task kinds and recovery semantics.

贡献指南

打开贡献指南

评估

这个 Issue 还没有评估数据。

把新 issue 发到你的邮箱

精选适合新手参与的 GitHub issue 摘要。