Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 0 additions & 2 deletions go.sum
Original file line number Diff line number Diff line change
Expand Up @@ -139,8 +139,6 @@ github.com/mvm-sh/mvm v0.5.0 h1:XWII2Y8RLEzvFMuBWSacg6KLUTUp2weBin4lUlFrV+Y=
github.com/mvm-sh/mvm v0.5.0/go.mod h1:2h9+ibS1DzdkMRNjGVs+pkU0blZZXDrEIUpcI93p4XA=
github.com/ncruces/go-strftime v1.0.0 h1:HMFp8mLCTPp341M/ZnA4qaf7ZlsbTc+miZjCLOFAw7w=
github.com/ncruces/go-strftime v1.0.0/go.mod h1:Fwc5htZGVVkseilnfgOVb9mKy6w1naJmn9CehxcKcls=
github.com/odvcencio/gotreesitter v0.47.0 h1:M4n0d9JPqHUGeZubI8Rzd21cxKqCwA33ULSqoFFznGc=
github.com/odvcencio/gotreesitter v0.47.0/go.mod h1:hBVkghd0paaYAVwd2087vfwdeU984bQbMo9LvpE0moo=
github.com/odvcencio/gotreesitter v0.47.1 h1:legFCs1A3HIpBNmaGW5oYj2QexAouxTshBlGifl9HSw=
github.com/odvcencio/gotreesitter v0.47.1/go.mod h1:hBVkghd0paaYAVwd2087vfwdeU984bQbMo9LvpE0moo=
github.com/oklog/ulid/v2 v2.1.1 h1:suPZ4ARWLOJLegGFiZZ1dFAkqzhMjL3J1TzI+5wHz8s=
Expand Down
191 changes: 165 additions & 26 deletions internal/terminal/agent_tasks.go
Original file line number Diff line number Diff line change
Expand Up @@ -669,6 +669,13 @@ func (app *App) postAgentTaskStreamEvent(ctx context.Context, event *database.Ta
}

func (app *App) handleAgentTaskTerminalEvent(ctx context.Context, taskID string) {
if app.inspectingWhilePromptRuns() && app.runtime != nil {
task, found, err := app.runtime.AgentTask(ctx, taskID)
if err == nil && found {
app.deliverAgentTaskCompletion(ctx, task)
}
}

if len(app.agentTaskSessionStack) == 0 {
app.refreshVisibleAgentTasks(ctx)

Expand Down Expand Up @@ -951,7 +958,31 @@ func (app *App) deliverAgentTaskCompletion(ctx context.Context, task *database.A
return
}

app.deliverAgentTaskCompletionText(ctx, task.Task.ID, completion)
if !app.withSessionView(task.Task.OwnerSessionID, func() {
app.deliverAgentTaskCompletionText(ctx, task.Task.ID, completion)
}) {
app.setStatus("agent result owner view is unavailable")
}
}

func (app *App) deliverAgentTaskCompletionEvent(ctx context.Context, taskID, completion string) {
ownerSessionID := app.sessionID
if app.runtime != nil {
task, found, err := app.runtime.AgentTask(ctx, taskID)
if err != nil || !found || task.Task.OwnerSessionID == "" {
app.setStatus("agent result owner could not be resolved")

return
}

ownerSessionID = task.Task.OwnerSessionID
}

if !app.withSessionView(ownerSessionID, func() {
app.deliverAgentTaskCompletionText(ctx, taskID, completion)
}) {
app.setStatus("agent result owner view is unavailable")
}
}

func (app *App) deliverAgentTaskCompletionText(ctx context.Context, taskID, completion string) {
Expand Down Expand Up @@ -1454,12 +1485,8 @@ func taskMeta(task *database.TaskEntity, now time.Time) string {
}

func (app *App) inspectAgentTask(ctx context.Context, taskID string) error {
if app.busy() || app.activePrompt != nil {
return errors.New("cannot inspect an agent task while a prompt is active")
}

if app.runtime == nil {
return terminalError(errors.New("runtime is not configured"), agentTaskLoadOperation)
if err := app.validateAgentTaskInspection(); err != nil {
return err
}

task, found, err := app.runtime.AgentTask(ctx, taskID)
Expand All @@ -1480,34 +1507,96 @@ func (app *App) inspectAgentTask(ctx context.Context, taskID string) error {
)
}

settings, settingsFound, err := app.sessionSettings(ctx, task.ChildSessionID)
if app.activePrompt != nil && len(app.agentTaskSessionStack) > 0 &&
app.activePrompt.SessionID == app.sessionID {
return errors.New("cannot leave an inspected agent session while its prompt is active")
}

if err := app.switchToAgentTaskSession(
ctx,
task.ChildSessionID,
nextSessionStack,
!isTerminalAgentTaskState(task.Task.State),
); err != nil {
return err
}

app.watchInspectedTaskIfRunning(ctx, task)

app.closePanel()
app.addSystemMessage("inspecting agent task: " + taskID + "; use /agents back to return")

return nil
}

func (app *App) validateAgentTaskInspection() error {
if app.authWorking || app.compacting || (app.working && app.activePrompt == nil) {
return errors.New("cannot inspect an agent task while another operation is active")
}

if app.runtime == nil {
return terminalError(errors.New("runtime is not configured"), agentTaskLoadOperation)
}

return nil
}

func (app *App) switchToAgentTaskSession(
ctx context.Context,
sessionID string,
sessionStack []string,
preserveTransientState bool,
) error {
settings, settingsFound, err := app.sessionSettings(ctx, sessionID)
if err != nil {
return terminalError(err, "load agent session")
}

messages, err := app.sessionMessages(ctx, task.ChildSessionID)
messages, err := app.sessionMessages(ctx, sessionID)
if err != nil {
return terminalError(err, "load agent session")
}

app.stopAgentTaskWatches()
app.agentTaskSessionStack = nextSessionStack
app.sessionID = task.ChildSessionID
app.pendingParentID = nil
app.resetMessages()
app.resetStreamingBlocks()
app.saveSessionView()
app.agentTaskSessionStack = sessionStack

if app.restoreSessionView(sessionID) {
promptHistory := app.promptHistory
promptHistoryDraft := app.promptHistoryDraft
promptHistoryIndex := app.promptHistoryIndex
app.transcript.History = nil
app.transcript.LineCache.reset()
app.appendSessionMessages(messages)
app.promptHistory = promptHistory
app.promptHistoryDraft = promptHistoryDraft
app.promptHistoryIndex = promptHistoryIndex
messages = nil

if !preserveTransientState {
app.resetStreamingBlocks()
app.streamingText = ""
app.streamingThinkingText = ""
app.streamedToolEvents = 0
}
} else {
app.sessionID = sessionID
app.pendingParentID = nil
app.resetMessages()
app.resetStreamingBlocks()
app.liveAgentCompletions = nil
app.queuedMessages = nil
app.hiddenQueuedMessages = nil
app.composerBuffer = tui.NewTextArea()
app.statusMessage = ""
}

if settingsFound {
app.applySessionSettings(&settings)
}

app.appendSessionMessages(messages)

app.watchInspectedTaskIfRunning(ctx, task)

app.closePanel()
app.addSystemMessage("inspecting agent task: " + taskID + "; use /agents back to return")

return nil
}

Expand Down Expand Up @@ -1562,17 +1651,24 @@ func (app *App) leaveAgentTaskSession(ctx context.Context) error {
}

app.stopAgentTaskWatches()
app.sessionID = parentSessionID
app.saveSessionView()
app.agentTaskSessionStack = app.agentTaskSessionStack[:last]
app.pendingParentID = nil
app.resetMessages()
app.resetStreamingBlocks()

if settingsFound {
app.applySessionSettings(&settings)
if app.restoreSessionView(parentSessionID) {
app.appendMissingSessionMessages(messages)
} else {
app.sessionID = parentSessionID
app.pendingParentID = nil
app.resetMessages()
app.resetStreamingBlocks()

if settingsFound {
app.applySessionSettings(&settings)
}

app.appendSessionMessages(messages)
}

app.appendSessionMessages(messages)
app.addSystemMessage("returned to parent session")

if len(app.agentTaskSessionStack) == 0 {
Expand All @@ -1584,6 +1680,49 @@ func (app *App) leaveAgentTaskSession(ctx context.Context) error {
return nil
}

func (app *App) appendMissingSessionMessages(messages []database.SessionMessageEntity) {
appended := false

for index := range messages {
message := &messages[index]
role := transcript.FromDatabaseRole(message.Role)
found := false

for historyIndex := range app.transcript.History {
history := &app.transcript.History[historyIndex]
if history.CreatedAt.Equal(message.CreatedAt) &&
history.Role == role && history.Content == message.Content {
found = true

break
}
}

if found {
continue
}

app.appendMessage(chatMessage{
CreatedAt: message.CreatedAt,
Role: role,
Content: message.Content,
})

appended = true

if message.Role == database.RoleUser {
app.recordPromptHistory(message.Content)
}
}

if appended {
slices.SortStableFunc(app.transcript.History, func(left, right chatMessage) int {
return left.CreatedAt.Compare(right.CreatedAt)
})
app.transcript.LineCache.reset()
}
}

func (app *App) resumeInspectedAgentTask(ctx context.Context, childSessionID string) {
ownerSessionID := app.agentTaskSessionStack[len(app.agentTaskSessionStack)-1]

Expand Down
Loading
Loading