mirror of
https://github.com/Ark0N/Codeman.git
synced 2026-10-05 23:19:43 +02:00
feat(session): say when a session is watching its own background work
An agent that arms a monitor, backgrounds a shell or hands a task to a cloud session is told to end its turn. The pane then falls quiet, Claude Code's idle_prompt notification arrives a minute later, and every surface files the session under NEEDS YOU with nothing for a human to answer. Claude states what it is still running on the last row of its screen (`⏵⏵ bypass permissions on · 1 monitor · ← for agents`). That row is now `capabilities.workDetect.watchingLine` in the CLI registry, guarded by compileVersionRegex() like every other config regex, and the idle probe reads it off the capture it already takes: `watchingLabel()` in session-activity.ts searches the last five lines only, so a session that PRINTS "1 monitor" is not mistaken for one running it. The label lands on Session.watching and rides toLightDetailedState() out to every surface. The phone overview, the desktop home rail and the rich sidebar rows wear it as a `watching` badge in the accent colour, beside the state pill and never in place of it: an agent can arm a monitor and ask a question in the same breath, and only the pill says which. Verified end to end against a throwaway session on an isolated beta instance: the payload carried `watching: "1 monitor"` once the turn ended, the badge rendered next to a yellow `waiting` pill, and both cleared when the monitor died. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
This commit is contained in:
co-authored by
Claude Opus 5
parent
9466acfc1a
commit
3f2cde2db7
@@ -91,3 +91,42 @@ export function isSustainedActivity(streak: ActivityStreak | null, streakMs: num
|
||||
export function isPaneQuiet(lastActivityAt: number, now: number, silenceMs: number = IDLE_SILENCE_MS): boolean {
|
||||
return now - lastActivityAt >= silenceMs;
|
||||
}
|
||||
|
||||
/**
|
||||
* How many lines at the foot of a pane capture may hold the background-work chip.
|
||||
*
|
||||
* Claude Code draws that chip on the last row of the screen, under its composer box
|
||||
* and under whatever status line the user configured, so five lines reach it with
|
||||
* room to spare. The ceiling is the point of the constant: the transcript above the
|
||||
* composer quotes arbitrary text, and a session that PRINTS the words "1 monitor"
|
||||
* must not be read as running one.
|
||||
*/
|
||||
export const WATCHING_TAIL_LINES = 5;
|
||||
|
||||
/**
|
||||
* What a pane says is still running in the background, e.g. `1 monitor` or `2 shells`.
|
||||
*
|
||||
* The CLI writes that chip while a monitor, a backgrounded shell or a cloud session it
|
||||
* started is still going, which is exactly the case where the agent has ended its turn
|
||||
* without wanting anything from the user. `pattern` comes from the CLI's own registry
|
||||
* entry (`capabilities.workDetect.watchingLine`); group 1 is the label when the pattern
|
||||
* declares one, and the whole match stands in when it does not.
|
||||
*
|
||||
* @returns the label, or null when the pane shows no background work
|
||||
*/
|
||||
export function watchingLabel(paneText: string | null | undefined, pattern: RegExp): string | null {
|
||||
if (!paneText) return null;
|
||||
const lines = paneText
|
||||
.split('\n')
|
||||
.map((line) => line.trimEnd())
|
||||
.filter((line) => line !== '');
|
||||
if (lines.length === 0) return null;
|
||||
// A pattern compiled by compileVersionRegex() never carries the `g` flag, but a caller
|
||||
// reaching in from a test or a config reload might, and a stale lastIndex would make
|
||||
// the same screen match every other call.
|
||||
pattern.lastIndex = 0;
|
||||
const match = pattern.exec(lines.slice(-WATCHING_TAIL_LINES).join('\n'));
|
||||
if (!match) return null;
|
||||
const label = (match[1] ?? match[0]).trim();
|
||||
return label === '' ? null : label;
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user