feat(agentic): add session command aliases
Co-Authored-By: Virgil <virgil@lethean.io>
This commit is contained in:
parent
aa7800781d
commit
6e5f4c5d37
4 changed files with 204 additions and 0 deletions
|
|
@ -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()
|
||||
}
|
||||
|
|
|
|||
78
pkg/agentic/commands_session.go
Normal file
78
pkg/agentic/commands_session.go
Normal 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}
|
||||
}
|
||||
123
pkg/agentic/commands_session_test.go
Normal file
123
pkg/agentic/commands_session_test.go
Normal 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")
|
||||
}
|
||||
|
|
@ -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")
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue