mirror of
https://github.com/Tencent/WeKnora.git
synced 2026-08-30 16:53:21 +08:00
fix(sandbox): pin Docker sessions so skill artifacts get collected
ArtifactCollector only drains /workspace/output from the pinned sandbox config. Docker is session-persistent like Cube/E2B, but execution skipped the pin, so generated HTML showed as unavailable after the turn.
This commit is contained in:
@@ -96,3 +96,23 @@ func TestArtifactSessionSourceSkipsUnpinnedSession(t *testing.T) {
|
||||
|
||||
require.Nil(t, collector.sessionSource(context.Background(), "s-1"))
|
||||
}
|
||||
|
||||
// Docker (and other named backends) pin the workspace config on first
|
||||
// execution. Collection must follow that pin rather than treating the
|
||||
// session as having no sandbox.
|
||||
func TestArtifactSessionSourceResolvesNamedPin(t *testing.T) {
|
||||
pinner := NewSessionSandboxPinner(newPinTestDB(t))
|
||||
ctx := context.WithValue(context.Background(), types.TenantIDContextKey, uint64(7))
|
||||
_, err := pinner.Pin(ctx, "s-1", "cfg-docker")
|
||||
require.NoError(t, err)
|
||||
|
||||
named := &artifactFallbackManager{source: &fakeSandboxSource{}}
|
||||
collector := &ArtifactCollector{
|
||||
source: &fakeSandboxSource{},
|
||||
resolver: stubSandboxResolver{mgr: named},
|
||||
pinner: pinner,
|
||||
}
|
||||
|
||||
got := collector.sessionSource(ctx, "s-1")
|
||||
require.Equal(t, named, got)
|
||||
}
|
||||
|
||||
@@ -173,7 +173,10 @@ func resolveSandboxForExecution(
|
||||
if err != nil || mgr == nil {
|
||||
return mgr, configID, err
|
||||
}
|
||||
if mgr.GetType() != sandbox.SandboxTypeCube && mgr.GetType() != sandbox.SandboxTypeE2B {
|
||||
// Named backends (Cube, E2B, Docker) all keep a session-scoped sandbox.
|
||||
// Artifact collection and teardown resolve that sandbox from this pin, so
|
||||
// skipping Docker here leaves /workspace/output files uncollected.
|
||||
if !sandbox.IsNamedSandboxBackendType(string(mgr.GetType())) {
|
||||
return mgr, configID, nil
|
||||
}
|
||||
if pinner == nil || strings.TrimSpace(sessionID) == "" {
|
||||
|
||||
@@ -181,6 +181,26 @@ func TestResolveSandboxForExecutionPinsRemoteBackend(t *testing.T) {
|
||||
require.Equal(t, "cfg-cube", pinned)
|
||||
}
|
||||
|
||||
// Docker is a session-persistent remote backend, same as Cube/E2B. Skipping
|
||||
// the pin used to make ArtifactCollector treat the turn as "no live sandbox"
|
||||
// and leave generated HTML/files showing as unavailable in chat.
|
||||
func TestResolveSandboxForExecutionPinsDockerBackend(t *testing.T) {
|
||||
pinner := NewSessionSandboxPinner(newPinTestDB(t))
|
||||
want := &pinTestManager{typ: sandbox.SandboxTypeDocker}
|
||||
|
||||
got, configID, err := resolveSandboxForExecution(
|
||||
context.Background(), stubSandboxResolver{mgr: want}, nil, pinner,
|
||||
7, "s-1", "cfg-docker", nil,
|
||||
)
|
||||
|
||||
require.NoError(t, err)
|
||||
require.Same(t, want, got)
|
||||
require.Equal(t, "cfg-docker", configID)
|
||||
pinned, err := pinner.Read(context.Background(), "s-1")
|
||||
require.NoError(t, err)
|
||||
require.Equal(t, "cfg-docker", pinned)
|
||||
}
|
||||
|
||||
func TestResolveSandboxForExecutionKeepsExistingRemotePin(t *testing.T) {
|
||||
pinner := NewSessionSandboxPinner(newPinTestDB(t))
|
||||
_, err := pinner.Pin(context.Background(), "s-1", "cfg-existing")
|
||||
|
||||
Reference in New Issue
Block a user