From 19172194e2f51d8645fd70cd739da7d598593f30 Mon Sep 17 00:00:00 2001 From: Virgil Date: Wed, 1 Apr 2026 23:25:11 +0000 Subject: [PATCH] feat(agentic): add session artifact command aliases Co-Authored-By: Virgil --- pkg/agentic/commands_session.go | 47 +++++++++++++++++++++++++ pkg/agentic/commands_session_test.go | 52 ++++++++++++++++++++++++++++ 2 files changed, 99 insertions(+) diff --git a/pkg/agentic/commands_session.go b/pkg/agentic/commands_session.go index 528c159..eab0743 100644 --- a/pkg/agentic/commands_session.go +++ b/pkg/agentic/commands_session.go @@ -16,6 +16,8 @@ func (s *PrepSubsystem) registerSessionCommands() { c.Command("agentic:session/complete", core.Command{Description: "Mark a stored session completed with status, summary, and handoff notes", Action: s.cmdSessionEnd}) c.Command("session/log", core.Command{Description: "Add a work log entry to a stored session", Action: s.cmdSessionLog}) c.Command("agentic:session/log", core.Command{Description: "Add a work log entry to a stored session", Action: s.cmdSessionLog}) + c.Command("session/artifact", core.Command{Description: "Record a created, modified, deleted, or reviewed artifact for a stored session", Action: s.cmdSessionArtifact}) + c.Command("agentic:session/artifact", core.Command{Description: "Record a created, modified, deleted, or reviewed artifact for a stored session", Action: s.cmdSessionArtifact}) c.Command("session/resume", core.Command{Description: "Resume a paused or handed-off session from local cache", Action: s.cmdSessionResume}) c.Command("agentic: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}) @@ -153,6 +155,51 @@ func (s *PrepSubsystem) cmdSessionLog(options core.Options) core.Result { return core.Result{Value: output, OK: true} } +// core-agent session artifact ses-abc123 --path="pkg/agentic/session.go" --action=modified --description="Tracked session metadata" +func (s *PrepSubsystem) cmdSessionArtifact(options core.Options) core.Result { + sessionID := optionStringValue(options, "session_id", "session-id", "id", "_arg") + path := optionStringValue(options, "path") + action := optionStringValue(options, "action") + if sessionID == "" { + core.Print(nil, "usage: core-agent session artifact --path=\"pkg/agentic/session.go\" --action=modified [--description=\"...\"] [--metadata='{\"key\":\"value\"}']") + return core.Result{Value: core.E("agentic.cmdSessionArtifact", "session_id is required", nil), OK: false} + } + if path == "" { + core.Print(nil, "usage: core-agent session artifact --path=\"pkg/agentic/session.go\" --action=modified [--description=\"...\"] [--metadata='{\"key\":\"value\"}']") + return core.Result{Value: core.E("agentic.cmdSessionArtifact", "path is required", nil), OK: false} + } + if action == "" { + core.Print(nil, "usage: core-agent session artifact --path=\"pkg/agentic/session.go\" --action=modified [--description=\"...\"] [--metadata='{\"key\":\"value\"}']") + return core.Result{Value: core.E("agentic.cmdSessionArtifact", "action is required", nil), OK: false} + } + + result := s.handleSessionArtifact(s.commandContext(), core.NewOptions( + core.Option{Key: "session_id", Value: sessionID}, + core.Option{Key: "path", Value: path}, + core.Option{Key: "action", Value: action}, + core.Option{Key: "metadata", Value: optionAnyMapValue(options, "metadata")}, + core.Option{Key: "description", Value: optionStringValue(options, "description")}, + )) + if !result.OK { + err := commandResultError("agentic.cmdSessionArtifact", result) + core.Print(nil, "error: %v", err) + return core.Result{Value: err, OK: false} + } + + output, ok := result.Value.(SessionArtifactOutput) + if !ok { + err := core.E("agentic.cmdSessionArtifact", "invalid session artifact output", nil) + core.Print(nil, "error: %v", err) + return core.Result{Value: err, OK: false} + } + + core.Print(nil, "session: %s", sessionID) + core.Print(nil, "path: %s", path) + core.Print(nil, "action: %s", action) + core.Print(nil, "artifact: %s", output.Artifact) + return core.Result{Value: output, OK: true} +} + func (s *PrepSubsystem) cmdSessionResume(options core.Options) core.Result { sessionID := optionStringValue(options, "session_id", "session-id", "id", "_arg") if sessionID == "" { diff --git a/pkg/agentic/commands_session_test.go b/pkg/agentic/commands_session_test.go index 2c8047c..218f4b7 100644 --- a/pkg/agentic/commands_session_test.go +++ b/pkg/agentic/commands_session_test.go @@ -27,6 +27,8 @@ func TestCommandsSession_RegisterSessionCommands_Good(t *testing.T) { assert.Contains(t, c.Commands(), "agentic:session/complete") assert.Contains(t, c.Commands(), "session/log") assert.Contains(t, c.Commands(), "agentic:session/log") + assert.Contains(t, c.Commands(), "session/artifact") + assert.Contains(t, c.Commands(), "agentic:session/artifact") assert.Contains(t, c.Commands(), "session/resume") assert.Contains(t, c.Commands(), "agentic:session/resume") assert.Contains(t, c.Commands(), "session/replay") @@ -226,6 +228,56 @@ func TestCommandsSession_CmdSessionLog_Ugly_CorruptedCacheFallsBackToRemoteError assert.Contains(t, result.Value.(error).Error(), "no platform API key configured") } +func TestCommandsSession_CmdSessionArtifact_Good(t *testing.T) { + dir := t.TempDir() + t.Setenv("CORE_WORKSPACE", dir) + + s := newTestPrep(t) + require.NoError(t, writeSessionCache(&Session{ + SessionID: "ses-artifact", + AgentType: "codex", + Status: "active", + })) + + result := s.cmdSessionArtifact(core.NewOptions( + core.Option{Key: "session_id", Value: "ses-artifact"}, + core.Option{Key: "path", Value: "pkg/agentic/session.go"}, + core.Option{Key: "action", Value: "modified"}, + core.Option{Key: "description", Value: "Tracked session metadata"}, + core.Option{Key: "metadata", Value: map[string]any{"repo": "go-agent"}}, + )) + require.True(t, result.OK) + + output, ok := result.Value.(SessionArtifactOutput) + require.True(t, ok) + assert.True(t, output.Success) + assert.Equal(t, "pkg/agentic/session.go", output.Artifact) + + cached, err := readSessionCache("ses-artifact") + require.NoError(t, err) + require.NotNil(t, cached) + require.Len(t, cached.Artifacts, 1) + assert.Equal(t, "modified", cached.Artifacts[0]["action"]) + assert.Equal(t, "pkg/agentic/session.go", cached.Artifacts[0]["path"]) + metadata, ok := cached.Artifacts[0]["metadata"].(map[string]any) + require.True(t, ok) + assert.Equal(t, "Tracked session metadata", metadata["description"]) + assert.Equal(t, "go-agent", metadata["repo"]) +} + +func TestCommandsSession_CmdSessionArtifact_Bad_MissingPath(t *testing.T) { + s := newTestPrep(t) + + result := s.cmdSessionArtifact(core.NewOptions( + core.Option{Key: "session_id", Value: "ses-artifact"}, + core.Option{Key: "action", Value: "modified"}, + )) + + assert.False(t, result.OK) + require.Error(t, result.Value.(error)) + assert.Contains(t, result.Value.(error).Error(), "path is required") +} + func TestCommandsSession_CmdSessionResume_Good(t *testing.T) { dir := t.TempDir() t.Setenv("CORE_WORKSPACE", dir)