mirror of
https://mirror.skon.top/github.com/code-yeongyu/oh-my-opencode
synced 2026-05-01 03:59:23 +08:00
fix(session-notification): defer platform detection and background checks
This commit is contained in:
31
src/hooks/session-notification-init.ts
Normal file
31
src/hooks/session-notification-init.ts
Normal file
@@ -0,0 +1,31 @@
|
||||
import type { Platform } from "./session-notification-sender"
|
||||
import * as sessionNotificationSender from "./session-notification-sender"
|
||||
import { startBackgroundCheck } from "./session-notification-utils"
|
||||
|
||||
export function createSessionNotificationInit() {
|
||||
let platform: Platform | null = null
|
||||
let defaultSoundPath: string | null = null
|
||||
let started = false
|
||||
|
||||
function initialize(): { platform: Platform; defaultSoundPath: string } {
|
||||
if (!platform) {
|
||||
platform = sessionNotificationSender.detectPlatform()
|
||||
}
|
||||
if (!defaultSoundPath) {
|
||||
defaultSoundPath = sessionNotificationSender.getDefaultSoundPath(platform)
|
||||
}
|
||||
if (!started) {
|
||||
startBackgroundCheck(platform)
|
||||
started = true
|
||||
}
|
||||
|
||||
return {
|
||||
platform,
|
||||
defaultSoundPath,
|
||||
}
|
||||
}
|
||||
|
||||
return {
|
||||
initialize,
|
||||
}
|
||||
}
|
||||
@@ -1,20 +1,12 @@
|
||||
import type { PluginInput } from "@opencode-ai/plugin"
|
||||
import { subagentSessions, getMainSessionID } from "../features/claude-code-session-state"
|
||||
import {
|
||||
startBackgroundCheck,
|
||||
} from "./session-notification-utils"
|
||||
import { buildReadyNotificationContent } from "./session-notification-content"
|
||||
import {
|
||||
type Platform,
|
||||
} from "./session-notification-sender"
|
||||
import { type Platform } from "./session-notification-sender"
|
||||
import * as sessionNotificationSender from "./session-notification-sender"
|
||||
import {
|
||||
getEventToolName,
|
||||
getQuestionText,
|
||||
getSessionID,
|
||||
} from "./session-notification-event-properties"
|
||||
import { getEventToolName, getQuestionText, getSessionID } from "./session-notification-event-properties"
|
||||
import { hasIncompleteTodos } from "./session-todo-status"
|
||||
import { createIdleNotificationScheduler } from "./session-notification-scheduler"
|
||||
import { createSessionNotificationInit } from "./session-notification-init"
|
||||
|
||||
interface SessionNotificationConfig {
|
||||
title?: string
|
||||
@@ -33,22 +25,15 @@ interface SessionNotificationConfig {
|
||||
/** Grace period in ms to ignore late-arriving activity events after scheduling (default: 100) */
|
||||
activityGracePeriodMs?: number
|
||||
}
|
||||
export function createSessionNotification(
|
||||
ctx: PluginInput,
|
||||
config: SessionNotificationConfig = {}
|
||||
) {
|
||||
const currentPlatform: Platform = sessionNotificationSender.detectPlatform()
|
||||
const defaultSoundPath = sessionNotificationSender.getDefaultSoundPath(currentPlatform)
|
||||
|
||||
startBackgroundCheck(currentPlatform)
|
||||
|
||||
export function createSessionNotification(ctx: PluginInput, config: SessionNotificationConfig = {}) {
|
||||
const mergedConfig = {
|
||||
title: "OpenCode",
|
||||
message: "Agent is ready for input",
|
||||
questionMessage: "Agent is asking a question",
|
||||
permissionMessage: "Agent needs permission to continue",
|
||||
playSound: false,
|
||||
soundPath: defaultSoundPath,
|
||||
soundPath: "",
|
||||
idleConfirmationDelay: 1500,
|
||||
skipIfIncompleteTodos: true,
|
||||
maxTrackedSessions: 100,
|
||||
@@ -56,22 +41,18 @@ export function createSessionNotification(
|
||||
...config,
|
||||
}
|
||||
|
||||
const sessionNotificationInit = createSessionNotificationInit()
|
||||
let currentPlatform: Platform | null = null
|
||||
let defaultSoundPath = mergedConfig.soundPath
|
||||
|
||||
const scheduler = createIdleNotificationScheduler({
|
||||
ctx,
|
||||
platform: currentPlatform,
|
||||
platform: "unsupported",
|
||||
config: mergedConfig,
|
||||
hasIncompleteTodos,
|
||||
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,
|
||||
)
|
||||
if (typeof hookCtx.client.session.get !== "function" && typeof hookCtx.client.session.messages !== "function") {
|
||||
await sessionNotificationSender.sendSessionNotification(hookCtx, platform, mergedConfig.title, mergedConfig.message)
|
||||
return
|
||||
}
|
||||
|
||||
@@ -90,6 +71,15 @@ export function createSessionNotification(
|
||||
const PERMISSION_EVENTS = new Set(["permission.ask", "permission.asked", "permission.updated", "permission.requested"])
|
||||
const PERMISSION_HINT_PATTERN = /\b(permission|approve|approval|allow|deny|consent)\b/i
|
||||
|
||||
const ensureNotificationPlatform = (): Platform => {
|
||||
if (currentPlatform) return currentPlatform
|
||||
|
||||
const initialized = sessionNotificationInit.initialize()
|
||||
currentPlatform = initialized.platform
|
||||
defaultSoundPath = initialized.defaultSoundPath || mergedConfig.soundPath
|
||||
return currentPlatform
|
||||
}
|
||||
|
||||
const shouldNotifyForSession = (sessionID: string): boolean => {
|
||||
if (subagentSessions.has(sessionID)) return false
|
||||
|
||||
@@ -102,16 +92,12 @@ export function createSessionNotification(
|
||||
}
|
||||
|
||||
return async ({ event }: { event: { type: string; properties?: unknown } }) => {
|
||||
if (currentPlatform === "unsupported") return
|
||||
|
||||
const props = event.properties as Record<string, unknown> | undefined
|
||||
|
||||
if (event.type === "session.created") {
|
||||
const info = props?.info as Record<string, unknown> | undefined
|
||||
const sessionID = info?.id as string | undefined
|
||||
if (sessionID) {
|
||||
scheduler.markSessionActivity(sessionID)
|
||||
}
|
||||
if (sessionID) scheduler.markSessionActivity(sessionID)
|
||||
return
|
||||
}
|
||||
|
||||
@@ -119,6 +105,8 @@ export function createSessionNotification(
|
||||
const sessionID = getSessionID(props)
|
||||
if (!sessionID) return
|
||||
|
||||
const platform = ensureNotificationPlatform()
|
||||
if (platform === "unsupported") return
|
||||
if (!shouldNotifyForSession(sessionID)) return
|
||||
|
||||
scheduler.scheduleIdleNotification(sessionID)
|
||||
@@ -128,26 +116,22 @@ export function createSessionNotification(
|
||||
if (event.type === "message.updated") {
|
||||
const info = props?.info as Record<string, unknown> | undefined
|
||||
const sessionID = getSessionID({ ...props, info })
|
||||
if (sessionID) {
|
||||
scheduler.markSessionActivity(sessionID)
|
||||
}
|
||||
if (sessionID) scheduler.markSessionActivity(sessionID)
|
||||
return
|
||||
}
|
||||
|
||||
if (PERMISSION_EVENTS.has(event.type)) {
|
||||
const sessionID = getSessionID(props)
|
||||
if (!sessionID) return
|
||||
|
||||
const platform = ensureNotificationPlatform()
|
||||
if (platform === "unsupported") return
|
||||
if (!shouldNotifyForSession(sessionID)) return
|
||||
|
||||
scheduler.markSessionActivity(sessionID)
|
||||
await sessionNotificationSender.sendSessionNotification(
|
||||
ctx,
|
||||
currentPlatform,
|
||||
mergedConfig.title,
|
||||
mergedConfig.permissionMessage,
|
||||
)
|
||||
if (mergedConfig.playSound && mergedConfig.soundPath) {
|
||||
await sessionNotificationSender.playSessionNotificationSound(ctx, currentPlatform, mergedConfig.soundPath)
|
||||
await sessionNotificationSender.sendSessionNotification(ctx, platform, mergedConfig.title, mergedConfig.permissionMessage)
|
||||
if (mergedConfig.playSound && defaultSoundPath) {
|
||||
await sessionNotificationSender.playSessionNotificationSound(ctx, platform, defaultSoundPath)
|
||||
}
|
||||
return
|
||||
}
|
||||
@@ -160,16 +144,16 @@ export function createSessionNotification(
|
||||
if (event.type === "tool.execute.before") {
|
||||
const toolName = getEventToolName(props)?.toLowerCase()
|
||||
if (toolName && QUESTION_TOOLS.has(toolName)) {
|
||||
const platform = ensureNotificationPlatform()
|
||||
if (platform === "unsupported") return
|
||||
if (!shouldNotifyForSession(sessionID)) return
|
||||
|
||||
const questionText = getQuestionText(props)
|
||||
const message = PERMISSION_HINT_PATTERN.test(questionText)
|
||||
? mergedConfig.permissionMessage
|
||||
: mergedConfig.questionMessage
|
||||
const message = PERMISSION_HINT_PATTERN.test(questionText) ? mergedConfig.permissionMessage : mergedConfig.questionMessage
|
||||
|
||||
await sessionNotificationSender.sendSessionNotification(ctx, currentPlatform, mergedConfig.title, message)
|
||||
if (mergedConfig.playSound && mergedConfig.soundPath) {
|
||||
await sessionNotificationSender.playSessionNotificationSound(ctx, currentPlatform, mergedConfig.soundPath)
|
||||
await sessionNotificationSender.sendSessionNotification(ctx, platform, mergedConfig.title, message)
|
||||
if (mergedConfig.playSound && defaultSoundPath) {
|
||||
await sessionNotificationSender.playSessionNotificationSound(ctx, platform, defaultSoundPath)
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -179,9 +163,7 @@ export function createSessionNotification(
|
||||
|
||||
if (event.type === "session.deleted") {
|
||||
const sessionInfo = props?.info as { id?: string } | undefined
|
||||
if (sessionInfo?.id) {
|
||||
scheduler.deleteSession(sessionInfo.id)
|
||||
}
|
||||
if (sessionInfo?.id) scheduler.deleteSession(sessionInfo.id)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user