feat(agentic): add session command aliases

Co-Authored-By: Virgil <virgil@lethean.io>
This commit is contained in:
Virgil 2026-04-01 19:28:56 +00:00
parent aa7800781d
commit 6e5f4c5d37
4 changed files with 204 additions and 0 deletions

View file

@ -42,6 +42,7 @@ func (s *PrepSubsystem) registerCommands(ctx context.Context) {
c.Command("prompt", core.Command{Description: "Build and display an agent prompt for a repo", Action: s.cmdPrompt})
c.Command("extract", core.Command{Description: "Extract a workspace template to a directory", Action: s.cmdExtract})
s.registerPlanCommands()
s.registerSessionCommands()
s.registerTaskCommands()
s.registerLanguageCommands()
}

View file

@ -0,0 +1,78 @@
// SPDX-License-Identifier: EUPL-1.2
package agentic
import (
core "dappco.re/go/core"
)
func (s *PrepSubsystem) registerSessionCommands() {
c := s.Core()
c.Command("session/resume", core.Command{Description: "Resume a paused or handed-off session from local cache", Action: s.cmdSessionResume})
c.Command("session/replay", core.Command{Description: "Build replay context for a stored session", Action: s.cmdSessionReplay})
}
func (s *PrepSubsystem) cmdSessionResume(options core.Options) core.Result {
sessionID := optionStringValue(options, "session_id", "session-id", "id", "_arg")
if sessionID == "" {
core.Print(nil, "usage: core-agent session resume <session-id>")
return core.Result{Value: core.E("agentic.cmdSessionResume", "session_id is required", nil), OK: false}
}
result := s.handleSessionResume(s.commandContext(), core.NewOptions(
core.Option{Key: "session_id", Value: sessionID},
))
if !result.OK {
err := commandResultError("agentic.cmdSessionResume", result)
core.Print(nil, "error: %v", err)
return core.Result{Value: err, OK: false}
}
output, ok := result.Value.(SessionResumeOutput)
if !ok {
err := core.E("agentic.cmdSessionResume", "invalid session resume output", nil)
core.Print(nil, "error: %v", err)
return core.Result{Value: err, OK: false}
}
core.Print(nil, "session: %s", output.Session.SessionID)
core.Print(nil, "status: %s", output.Session.Status)
if len(output.HandoffContext) > 0 {
core.Print(nil, "handoff: %d item(s)", len(output.HandoffContext))
}
if len(output.RecentActions) > 0 {
core.Print(nil, "recent: %d action(s)", len(output.RecentActions))
}
if len(output.Artifacts) > 0 {
core.Print(nil, "artifacts: %d", len(output.Artifacts))
}
return core.Result{Value: output, OK: true}
}
func (s *PrepSubsystem) cmdSessionReplay(options core.Options) core.Result {
sessionID := optionStringValue(options, "session_id", "session-id", "id", "_arg")
if sessionID == "" {
core.Print(nil, "usage: core-agent session replay <session-id>")
return core.Result{Value: core.E("agentic.cmdSessionReplay", "session_id is required", nil), OK: false}
}
result := s.handleSessionReplay(s.commandContext(), core.NewOptions(
core.Option{Key: "session_id", Value: sessionID},
))
if !result.OK {
err := commandResultError("agentic.cmdSessionReplay", result)
core.Print(nil, "error: %v", err)
return core.Result{Value: err, OK: false}
}
output, ok := result.Value.(SessionReplayOutput)
if !ok {
err := core.E("agentic.cmdSessionReplay", "invalid session replay output", nil)
core.Print(nil, "error: %v", err)
return core.Result{Value: err, OK: false}
}
core.Print(nil, "session: %s", sessionID)
core.Print(nil, "context items: %d", len(output.ReplayContext))
return core.Result{Value: output, OK: true}
}

View file

@ -0,0 +1,123 @@
// SPDX-License-Identifier: EUPL-1.2
package agentic
import (
"testing"
"time"
core "dappco.re/go/core"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
)
func TestCommandsSession_RegisterSessionCommands_Good(t *testing.T) {
c := core.New(core.WithOption("name", "test"))
s := &PrepSubsystem{ServiceRuntime: core.NewServiceRuntime(c, AgentOptions{})}
s.registerSessionCommands()
assert.Contains(t, c.Commands(), "session/resume")
assert.Contains(t, c.Commands(), "session/replay")
}
func TestCommandsSession_CmdSessionResume_Good(t *testing.T) {
dir := t.TempDir()
t.Setenv("CORE_WORKSPACE", dir)
s := newTestPrep(t)
require.NoError(t, writeSessionCache(&Session{
SessionID: "ses-abc123",
AgentType: "codex",
Status: "paused",
ContextSummary: map[string]any{"repo": "go-io"},
WorkLog: []map[string]any{
{"type": "checkpoint", "message": "build passed"},
{"type": "decision", "message": "open PR"},
},
Artifacts: []map[string]any{
{"path": "pkg/agentic/session.go", "action": "modified"},
},
Handoff: map[string]any{
"summary": "Ready for review",
},
}))
result := s.cmdSessionResume(core.NewOptions(core.Option{Key: "session_id", Value: "ses-abc123"}))
require.True(t, result.OK)
output, ok := result.Value.(SessionResumeOutput)
require.True(t, ok)
assert.True(t, output.Success)
assert.Equal(t, "ses-abc123", output.Session.SessionID)
assert.Equal(t, "active", output.Session.Status)
assert.NotEmpty(t, output.HandoffContext)
assert.Len(t, output.RecentActions, 2)
assert.Len(t, output.Artifacts, 1)
}
func TestCommandsSession_CmdSessionResume_Bad_MissingSessionID(t *testing.T) {
s := newTestPrep(t)
result := s.cmdSessionResume(core.NewOptions())
assert.False(t, result.OK)
require.Error(t, result.Value.(error))
assert.Contains(t, result.Value.(error).Error(), "session_id is required")
}
func TestCommandsSession_CmdSessionResume_Ugly_CorruptedCacheFallsBackToRemoteError(t *testing.T) {
dir := t.TempDir()
t.Setenv("CORE_WORKSPACE", dir)
s := newTestPrep(t)
require.True(t, fs.EnsureDir(sessionCacheRoot()).OK)
require.True(t, fs.WriteAtomic(sessionCachePath("ses-bad"), "{not-json").OK)
result := s.cmdSessionResume(core.NewOptions(core.Option{Key: "session_id", Value: "ses-bad"}))
assert.False(t, result.OK)
require.Error(t, result.Value.(error))
assert.Contains(t, result.Value.(error).Error(), "no platform API key configured")
}
func TestCommandsSession_CmdSessionReplay_Good(t *testing.T) {
dir := t.TempDir()
t.Setenv("CORE_WORKSPACE", dir)
s := newTestPrep(t)
require.NoError(t, writeSessionCache(&Session{
SessionID: "ses-replay",
AgentType: "codex",
Status: "active",
WorkLog: []map[string]any{
{"type": "checkpoint", "message": "started", "timestamp": time.Now().Format(time.RFC3339)},
{"type": "decision", "message": "kept scope small", "timestamp": time.Now().Format(time.RFC3339)},
{"type": "error", "message": "flaky test", "timestamp": time.Now().Format(time.RFC3339)},
},
Artifacts: []map[string]any{
{"path": "pkg/agentic/commands_session.go", "action": "created"},
},
}))
result := s.cmdSessionReplay(core.NewOptions(core.Option{Key: "session_id", Value: "ses-replay"}))
require.True(t, result.OK)
output, ok := result.Value.(SessionReplayOutput)
require.True(t, ok)
assert.True(t, output.Success)
assert.Equal(t, "ses-replay", output.ReplayContext["session_id"])
assert.Contains(t, output.ReplayContext, "checkpoints")
assert.Contains(t, output.ReplayContext, "decisions")
assert.Contains(t, output.ReplayContext, "errors")
}
func TestCommandsSession_CmdSessionReplay_Bad_MissingSessionID(t *testing.T) {
s := newTestPrep(t)
result := s.cmdSessionReplay(core.NewOptions())
assert.False(t, result.OK)
require.Error(t, result.Value.(error))
assert.Contains(t, result.Value.(error).Error(), "session_id is required")
}

View file

@ -629,6 +629,8 @@ func TestPrep_OnStartup_Good_RegistersGenerateCommand(t *testing.T) {
assert.Contains(t, c.Commands(), "lang/list")
assert.Contains(t, c.Commands(), "plan-cleanup")
assert.Contains(t, c.Commands(), "plan/from-issue")
assert.Contains(t, c.Commands(), "session/resume")
assert.Contains(t, c.Commands(), "session/replay")
assert.Contains(t, c.Commands(), "review-queue")
assert.Contains(t, c.Commands(), "task")
assert.Contains(t, c.Commands(), "task/create")