Skip to content

feat: show script output in launcher and notifications after every run#322

Merged
Xoshbin merged 1 commit into
mainfrom
script-output-visibility
May 15, 2026
Merged

feat: show script output in launcher and notifications after every run#322
Xoshbin merged 1 commit into
mainfrom
script-output-visibility

Conversation

@Xoshbin

@Xoshbin Xoshbin commented May 15, 2026

Copy link
Copy Markdown
Owner

Scripts now keep their last-line output preview after they finish, on both
the success and failure paths, so users can always see how a script ended
without having to open the run detail view in time.

  • Capture Run.tailOutput at finalize from the in-memory OutputBuffer; keep
    the buffer alive past finalize so RunView can show the full output
    post-mortem and dismiss explicitly frees it via runs_dismiss.
  • Succeeded shell-scripts persist as "Done · {output}" rows in the Scripts
    section until dismissed (mirroring the kept-agents pattern); previously
    they vanished silently.
  • Failed-run rows and notifications now lead with the captured output tail
    instead of just the run label or raw error code.
  • New "Script finished" notification carries the tail preview and opens
    RunView with the run pre-selected; agent runs still notify only on
    failure.

Scripts now keep their last-line output preview after they finish, on both
the success and failure paths, so users can always see how a script ended
without having to open the run detail view in time.

- Capture Run.tailOutput at finalize from the in-memory OutputBuffer; keep
  the buffer alive past finalize so RunView can show the full output
  post-mortem and dismiss explicitly frees it via runs_dismiss.
- Succeeded shell-scripts persist as "Done · {output}" rows in the Scripts
  section until dismissed (mirroring the kept-agents pattern); previously
  they vanished silently.
- Failed-run rows and notifications now lead with the captured output tail
  instead of just the run label or raw error code.
- New "Script finished" notification carries the tail preview and opens
  RunView with the run pre-selected; agent runs still notify only on
  failure.

@gemini-code-assist gemini-code-assist Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Code Review

This pull request implements persistent tracking for shell script results, enabling users to view script status and output in the launcher until they are explicitly dismissed. Key updates include the capture and persistence of a 'tail output' preview for use in UI subtitles and notifications, along with logic to keep output buffers available post-mortem. Review feedback identifies a potential memory leak when runs are evicted from capped frontend slices, suggests optimizing backend tail-capture to avoid cloning large buffers, and provides a correction for the truncate function to properly handle multi-unit Unicode characters.

Comment on lines 212 to +222
].slice(0, UNACK_FAILED_CAP);
}

if (run.status === 'succeeded' && run.kind === 'shell-script') {
// Scripts persist after success so the user can read the output.
// Dedupe by subjectId when present so re-running the same script row
// collapses into one entry; anonymous (no subjectId) runs dedupe by id.
const filtered = run.subjectId
? this.unacknowledgedScriptResults.filter((r) => r.subjectId !== run.subjectId)
: this.unacknowledgedScriptResults.filter((r) => r.id !== run.id);
this.unacknowledgedScriptResults = [run, ...filtered].slice(0, UNACK_FAILED_CAP);

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

high

Similar to the failure slice, when an agent run is evicted from keptAgents or a script result is evicted from unacknowledgedScriptResults, the backend buffer is leaked. Ensure that any run dropped from these reactive slices also triggers a runs_dismiss call to free the in-memory output buffer.

Comment on lines +74 to +75
let snapshot = buffer.snapshot(id);
let tail = format_tail_output(&snapshot);

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

medium

buffer.snapshot(id) clones the entire output buffer (up to 10,000 lines) into a Vec<String> just to extract the last non-empty line. This is inefficient for runs with large output. Consider adding a method to OutputBuffer that retrieves only the tail or the last non-empty line directly from the VecDeque without cloning the entire collection.

Comment on lines +22 to +25
function truncate(text: string, max = NOTIFICATION_BODY_MAX): string {
if (text.length <= max) return text;
return text.slice(0, max - 1) + '…';
}

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

medium

The truncate function uses String.prototype.slice and String.prototype.length, which operate on UTF-16 code units. This can break multi-unit characters (like emojis) if they fall on the truncation boundary, resulting in malformed strings with dangling surrogates.

Suggested change
function truncate(text: string, max = NOTIFICATION_BODY_MAX): string {
if (text.length <= max) return text;
return text.slice(0, max - 1) + '…';
}
function truncate(text: string, max = NOTIFICATION_BODY_MAX): string {
const chars = Array.from(text);
if (chars.length <= max) return text;
return chars.slice(0, max - 1).join('') + '…';
}

@Xoshbin Xoshbin merged commit e4965b1 into main May 15, 2026
1 check passed
@Xoshbin Xoshbin deleted the script-output-visibility branch May 15, 2026 19:26
Xoshbin added a commit that referenced this pull request May 24, 2026
feat: show script output in launcher and notifications after every run
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant