-
Notifications
You must be signed in to change notification settings - Fork 12
trace-needs-input-marker -> main #951
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Changes from all commits
79e2549
0dbcf60
6b5dcd4
a026232
d709483
a2cd5d7
1e7d423
bad6ab2
229d316
1f460b2
7c27d3d
2103443
1acadf8
c8de87f
2ace3e0
67b47f6
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -196,6 +196,10 @@ export type PushPublisherSources = { | |
| title: string | null; | ||
| toolType?: string | null; | ||
| chatSessionId?: string | null; | ||
| status?: string | null; | ||
| runtimeState?: string | null; | ||
| settledAt?: string | null; | ||
| settleOverride?: "settled" | "active" | null; | ||
| } | null; | ||
| }; | ||
|
|
||
|
|
@@ -932,6 +936,19 @@ export function createPushPublisherService(deps: PushPublisherDeps) { | |
| runs.delete(run.sessionId); | ||
| continue; | ||
| } | ||
| const atRest = record.status !== "running" || record.runtimeState === "idle"; | ||
| if ( | ||
| atRest | ||
| && ( | ||
| record.settleOverride === "settled" | ||
| || (record.settleOverride !== "active" && record.settledAt) | ||
| ) | ||
| ) { | ||
| run.phase = "completed"; | ||
| recentRuns.set(run.sessionId, { ...run }); | ||
| runs.delete(run.sessionId); | ||
| continue; | ||
| } | ||
| run.title = record.title?.trim() || run.title || null; | ||
| run.agent = providerDisplayName(record.toolType) ?? run.agent ?? "CLI"; | ||
| run.metaResolved = true; | ||
|
|
@@ -1548,22 +1565,49 @@ export function createPushPublisherService(deps: PushPublisherDeps) { | |
| }; | ||
|
|
||
| /** | ||
| * OSC 133-derived terminal state for tracked CLI sessions. Feeds the Live | ||
| * Activity only — no alert pushes: a CLI agent returns to its prompt | ||
| * (waiting-input) after EVERY turn, so alerting on it would ping the user | ||
| * once per turn. Failure alerts stay with onPtyExit's non-zero-exit path. | ||
| * Terminal runtime state for tracked CLI sessions. Prompt/marker inference | ||
| * must never raise attention: only an explicit `ade chat ask` or a | ||
| * provider-structured pending input may publish `waiting_for_input`. | ||
| */ | ||
| const onCliRuntimeSignal = (scopeKey: string, signal: PushCliRuntimeSignal): void => { | ||
| if (disposed || !signal.sessionId) return; | ||
| const existing = runs.get(signal.sessionId); | ||
| const session = scopes.get(scopeKey)?.resolveCliSession?.(signal.sessionId) ?? null; | ||
| const atRest = session?.status !== "running" || signal.runtimeState === "idle"; | ||
| if ( | ||
| atRest | ||
| && ( | ||
| session?.settleOverride === "settled" | ||
| || (session?.settleOverride !== "active" && Boolean(session?.settledAt)) | ||
| ) | ||
| ) { | ||
| if (existing) { | ||
| existing.phase = "completed"; | ||
| existing.itemId = null; | ||
| markRunUpdated(existing); | ||
| recentRuns.set(signal.sessionId, { ...existing }); | ||
| runs.delete(signal.sessionId); | ||
| pendingAlerts = pendingAlerts.filter( | ||
| (alert) => | ||
| alert.dedupeKey !== `alert:${signal.sessionId}:approval` | ||
| && alert.dedupeKey !== `alert:${signal.sessionId}:question`, | ||
| ); | ||
| clearAlertDedupe(`alert:${signal.sessionId}:approval`); | ||
| clearAlertDedupe(`alert:${signal.sessionId}:question`); | ||
| scheduleFlush(true); | ||
| } | ||
| return; | ||
| } | ||
| // Exit/kill phases are owned by onPtyExit (which knows the exit code). | ||
| if (signal.runtimeState === "exited" || signal.runtimeState === "killed") return; | ||
| // Explicit/provider-structured attention owns this phase until the | ||
| // lifecycle event that resolves it. PTY heartbeats are observational and | ||
| // must not erase a real request. | ||
| if (existing?.phase === "waiting_for_input" || existing?.phase === "waiting_for_approval") return; | ||
|
arul28 marked this conversation as resolved.
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
When an explicit CLI ask is cleared through AGENTS.md reference: AGENTS.md:L35-L35 Useful? React with 👍 / 👎. |
||
| // `idle` = no output for 12s with no OSC prompt marker — we can't prove | ||
| // the CLI is working OR at a prompt, so publish it as `stale` (dimmed, | ||
| // not counted active) instead of overstating it as a live running row. | ||
| const phase: AgentRunPhase = signal.runtimeState === "waiting-input" | ||
| ? "waiting_for_input" | ||
| : signal.runtimeState === "idle" | ||
| const phase: AgentRunPhase = signal.runtimeState === "waiting-input" || signal.runtimeState === "idle" | ||
| ? "stale" | ||
| : "running"; | ||
| // Signals re-fire on a ~10s heartbeat; only a phase change is worth a | ||
|
|
@@ -1886,6 +1930,43 @@ export function createPushPublisherService(deps: PushPublisherDeps) { | |
| scheduleFlush(true); | ||
| }, | ||
|
|
||
| handleSessionAttentionResolved(scopeKey: string | null, sessionId: string): void { | ||
| if (disposed || !sessionId) return; | ||
| const run = runs.get(sessionId); | ||
| if (!run || (scopeKey != null && run.scopeKey !== scopeKey)) return; | ||
| if (run.phase !== "waiting_for_input" && run.phase !== "waiting_for_approval") return; | ||
| run.phase = "running"; | ||
|
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
In the socket AGENTS.md reference: AGENTS.md:L35-L35 Useful? React with 👍 / 👎. |
||
| run.itemId = null; | ||
| markRunUpdated(run); | ||
| pendingAlerts = pendingAlerts.filter( | ||
| (alert) => | ||
| alert.dedupeKey !== `alert:${sessionId}:approval` | ||
| && alert.dedupeKey !== `alert:${sessionId}:question`, | ||
| ); | ||
| clearAlertDedupe(`alert:${sessionId}:approval`); | ||
| clearAlertDedupe(`alert:${sessionId}:question`); | ||
| scheduleFlush(true); | ||
| }, | ||
|
|
||
| handleSessionSettled(scopeKey: string | null, sessionId: string): void { | ||
| if (disposed || !sessionId) return; | ||
| const run = runs.get(sessionId); | ||
| if (!run || (scopeKey != null && run.scopeKey !== scopeKey)) return; | ||
| run.phase = "completed"; | ||
| run.itemId = null; | ||
| markRunUpdated(run); | ||
| recentRuns.set(sessionId, { ...run }); | ||
|
Comment on lines
+1955
to
+1958
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
When a tracked CLI is settled while its PTY remains active, this records AGENTS.md reference: AGENTS.md:L35-L35 Useful? React with 👍 / 👎. |
||
| runs.delete(sessionId); | ||
|
Comment on lines
+1958
to
+1959
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
When a tracked CLI is settled while its PTY remains live, deleting it from Useful? React with 👍 / 👎. |
||
| pendingAlerts = pendingAlerts.filter( | ||
| (alert) => | ||
| alert.dedupeKey !== `alert:${sessionId}:approval` | ||
| && alert.dedupeKey !== `alert:${sessionId}:question`, | ||
| ); | ||
| clearAlertDedupe(`alert:${sessionId}:approval`); | ||
| clearAlertDedupe(`alert:${sessionId}:question`); | ||
| scheduleFlush(true); | ||
| }, | ||
|
|
||
| /** Force a flush soon (e.g. right after a device registers). */ | ||
| poke(): void { | ||
| scheduleFlush(true); | ||
|
|
||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This listener runs after every session metadata change, so a chat that retains
settledAtduring an active scheduled/background turn is treated as newly settled. For example, after the chat event publishes the background turn as running, a title or status-note update emitsterminal_session_changed; this condition then callshandleSessionSettled, removes the active push run, and ends its Live Activity even though the canonical lifecycle intentionally considers the session running until the turn returns to rest. Trigger termination only when the mutation actually enters settlement, or include the current at-rest runtime condition. This affects the ADE CLI runtime used by both headless and desktop socket-backed paths.AGENTS.md reference: AGENTS.md:L35-L35
Useful? React with 👍 / 👎.