feat(session-notification): include session context in ready notifications

Ultraworked with [Sisyphus](https://github.com/code-yeongyu/oh-my-openagent)

Co-authored-by: Sisyphus <clio-agent@sisyphuslabs.ai>
This commit is contained in:
YeonGyu-Kim
2026-03-12 15:29:21 +09:00
parent 943f31f460
commit 04b0d62a55
2 changed files with 40 additions and 10 deletions
+17 -9
View File
@@ -2,8 +2,6 @@ import type { PluginInput } from "@opencode-ai/plugin"
import type { Platform } from "./session-notification-sender" import type { Platform } from "./session-notification-sender"
type SessionNotificationConfig = { type SessionNotificationConfig = {
title: string
message: string
playSound: boolean playSound: boolean
soundPath: string soundPath: string
idleConfirmationDelay: number idleConfirmationDelay: number
@@ -18,7 +16,7 @@ export function createIdleNotificationScheduler(options: {
platform: Platform platform: Platform
config: SessionNotificationConfig config: SessionNotificationConfig
hasIncompleteTodos: (ctx: PluginInput, sessionID: string) => Promise<boolean> hasIncompleteTodos: (ctx: PluginInput, sessionID: string) => Promise<boolean>
send: (ctx: PluginInput, platform: Platform, title: string, message: string) => Promise<void> send: (ctx: PluginInput, platform: Platform, sessionID: string) => Promise<void>
playSound: (ctx: PluginInput, platform: Platform, soundPath: string) => Promise<void> playSound: (ctx: PluginInput, platform: Platform, soundPath: string) => Promise<void>
}) { }) {
const notifiedSessions = new Set<string>() const notifiedSessions = new Set<string>()
@@ -34,23 +32,33 @@ export function createIdleNotificationScheduler(options: {
const maxSessions = options.config.maxTrackedSessions const maxSessions = options.config.maxTrackedSessions
if (notifiedSessions.size > maxSessions) { if (notifiedSessions.size > maxSessions) {
const sessionsToRemove = Array.from(notifiedSessions).slice(0, notifiedSessions.size - maxSessions) const sessionsToRemove = Array.from(notifiedSessions).slice(0, notifiedSessions.size - maxSessions)
sessionsToRemove.forEach((id) => notifiedSessions.delete(id)) sessionsToRemove.forEach((id) => {
notifiedSessions.delete(id)
})
} }
if (sessionActivitySinceIdle.size > maxSessions) { if (sessionActivitySinceIdle.size > maxSessions) {
const sessionsToRemove = Array.from(sessionActivitySinceIdle).slice(0, sessionActivitySinceIdle.size - maxSessions) const sessionsToRemove = Array.from(sessionActivitySinceIdle).slice(0, sessionActivitySinceIdle.size - maxSessions)
sessionsToRemove.forEach((id) => sessionActivitySinceIdle.delete(id)) sessionsToRemove.forEach((id) => {
sessionActivitySinceIdle.delete(id)
})
} }
if (notificationVersions.size > maxSessions) { if (notificationVersions.size > maxSessions) {
const sessionsToRemove = Array.from(notificationVersions.keys()).slice(0, notificationVersions.size - maxSessions) const sessionsToRemove = Array.from(notificationVersions.keys()).slice(0, notificationVersions.size - maxSessions)
sessionsToRemove.forEach((id) => notificationVersions.delete(id)) sessionsToRemove.forEach((id) => {
notificationVersions.delete(id)
})
} }
if (executingNotifications.size > maxSessions) { if (executingNotifications.size > maxSessions) {
const sessionsToRemove = Array.from(executingNotifications).slice(0, executingNotifications.size - maxSessions) const sessionsToRemove = Array.from(executingNotifications).slice(0, executingNotifications.size - maxSessions)
sessionsToRemove.forEach((id) => executingNotifications.delete(id)) sessionsToRemove.forEach((id) => {
executingNotifications.delete(id)
})
} }
if (scheduledAt.size > maxSessions) { if (scheduledAt.size > maxSessions) {
const sessionsToRemove = Array.from(scheduledAt.keys()).slice(0, scheduledAt.size - maxSessions) const sessionsToRemove = Array.from(scheduledAt.keys()).slice(0, scheduledAt.size - maxSessions)
sessionsToRemove.forEach((id) => scheduledAt.delete(id)) sessionsToRemove.forEach((id) => {
scheduledAt.delete(id)
})
} }
} }
@@ -128,7 +136,7 @@ export function createIdleNotificationScheduler(options: {
notifiedSessions.add(sessionID) notifiedSessions.add(sessionID)
await options.send(options.ctx, options.platform, options.config.title, options.config.message) await options.send(options.ctx, options.platform, sessionID)
if (options.config.playSound && options.config.soundPath) { if (options.config.playSound && options.config.soundPath) {
await options.playSound(options.ctx, options.platform, options.config.soundPath) await options.playSound(options.ctx, options.platform, options.config.soundPath)
+23 -1
View File
@@ -3,6 +3,7 @@ import { subagentSessions, getMainSessionID } from "../features/claude-code-sess
import { import {
startBackgroundCheck, startBackgroundCheck,
} from "./session-notification-utils" } from "./session-notification-utils"
import { buildReadyNotificationContent } from "./session-notification-content"
import { import {
type Platform, type Platform,
} from "./session-notification-sender" } from "./session-notification-sender"
@@ -55,7 +56,28 @@ export function createSessionNotification(
platform: currentPlatform, platform: currentPlatform,
config: mergedConfig, config: mergedConfig,
hasIncompleteTodos, hasIncompleteTodos,
send: sessionNotificationSender.sendSessionNotification, send: async (hookCtx, platform, sessionID) => {
if (
typeof hookCtx.client.session.get !== "function"
&& typeof hookCtx.client.session.messages !== "function"
) {
await sessionNotificationSender.sendSessionNotification(
hookCtx,
platform,
mergedConfig.title,
mergedConfig.message,
)
return
}
const content = await buildReadyNotificationContent(hookCtx, {
sessionID,
baseTitle: mergedConfig.title,
baseMessage: mergedConfig.message,
})
await sessionNotificationSender.sendSessionNotification(hookCtx, platform, content.title, content.message)
},
playSound: sessionNotificationSender.playSessionNotificationSound, playSound: sessionNotificationSender.playSessionNotificationSound,
}) })