@@ -12,8 +12,7 @@ import { truncate, extractTextFromParts } from "./utils"
1212const PLUGIN_VERSION = "0.1.7"
1313const NOTIFICATION_TITLE = "warp://cli-agent"
1414
15- function sendPermissionNotification ( perm : Permission , cwd : string ) : void {
16- const sessionId = perm . sessionID
15+ function buildPermissionPayload ( perm : Permission , cwd : string ) {
1716 const toolName = perm . type || "unknown"
1817 const metadata = perm . metadata || { }
1918
@@ -34,12 +33,11 @@ function sendPermissionNotification(perm: Permission, cwd: string): void {
3433 summary += `: ${ truncate ( toolPreview , 120 ) } `
3534 }
3635
37- const body = buildPayload ( "permission_request" , sessionId , cwd , {
36+ return buildPayload ( "permission_request" , perm . sessionID , cwd , {
3837 summary,
3938 tool_name : toolName ,
4039 tool_input : metadata ,
4140 } )
42- warpNotify ( NOTIFICATION_TITLE , body )
4341}
4442
4543export const WarpPlugin : Plugin = async ( { client, directory } ) => {
@@ -73,34 +71,49 @@ export const WarpPlugin: Plugin = async ({ client, directory }) => {
7371 console . error ( "[opencode-warp] failed to emit init log:" , err )
7472 } )
7573
74+ const subagentCache = new Map < string , boolean > ( )
75+
76+ async function isSubagentSession ( sessionId ?: string ) : Promise < boolean > {
77+ if ( ! sessionId ) return false
78+ if ( subagentCache . has ( sessionId ) ) return subagentCache . get ( sessionId ) !
79+ try {
80+ const session = await client . session . get ( {
81+ path : { id : sessionId } ,
82+ } )
83+ const result = ! ! session . data ?. parentID
84+ subagentCache . set ( sessionId , result )
85+ return result
86+ } catch {
87+ // If we can't fetch the session, fall through and notify anyway
88+ return false
89+ }
90+ }
91+
92+ async function maybeWarpNotify ( sessionId : string | undefined , body : string ) : Promise < void > {
93+ if ( await isSubagentSession ( sessionId ) ) return
94+ warpNotify ( NOTIFICATION_TITLE , body )
95+ }
96+
7697 return {
7798 event : async ( { event } : { event : Event } ) => {
7899 const cwd = directory || ""
79100
80101 switch ( event . type ) {
81102 case "session.created" : {
82- const sessionId = event . properties . info . id
83- const body = buildPayload ( "session_start" , sessionId , cwd , {
103+ const info = event . properties . info
104+ if ( info . parentID ) return
105+ const body = buildPayload ( "session_start" , info . id , cwd , {
84106 plugin_version : PLUGIN_VERSION ,
85107 } )
86- warpNotify ( NOTIFICATION_TITLE , body )
108+ await maybeWarpNotify ( info . id , body )
87109 return
88110 }
89111
90112 case "session.idle" : {
91113 const sessionId = event . properties . sessionID
92114
93- if ( sessionId ) {
94- try {
95- const session = await client . session . get ( {
96- path : { id : sessionId } ,
97- } )
98- if ( session . data ?. parentID ) return
99- } catch {
100- // If we can't fetch the session, fall through and notify anyway
101- }
102- }
103-
115+ // Fetch the conversation to extract last query and response
116+ // (port of on-stop.sh transcript parsing)
104117 let query = ""
105118 let response = ""
106119
@@ -138,28 +151,34 @@ export const WarpPlugin: Plugin = async ({ client, directory }) => {
138151 response : truncate ( response , 200 ) ,
139152 transcript_path : "" ,
140153 } )
141- warpNotify ( NOTIFICATION_TITLE , body )
154+ await maybeWarpNotify ( sessionId , body )
142155 return
143156 }
144157
145158 case "permission.updated" : {
146- sendPermissionNotification ( event . properties , cwd )
159+ await maybeWarpNotify (
160+ event . properties . sessionID ,
161+ buildPermissionPayload ( event . properties , cwd ) ,
162+ )
147163 return
148164 }
149165
150166 case "permission.replied" : {
151167 const { sessionID, response } = event . properties
152168 if ( response === "reject" ) return
153169 const body = buildPayload ( "permission_replied" , sessionID , cwd )
154- warpNotify ( NOTIFICATION_TITLE , body )
170+ await maybeWarpNotify ( sessionID , body )
155171 return
156172 }
157173
158174 default : {
159175 // permission.asked is listed in the opencode docs but has no SDK type.
160176 // Handle it with the same logic as permission.updated.
161177 if ( ( event as any ) . type === "permission.asked" ) {
162- sendPermissionNotification ( ( event as any ) . properties , cwd )
178+ await maybeWarpNotify (
179+ ( event as any ) . properties ?. sessionID ,
180+ buildPermissionPayload ( ( event as any ) . properties , cwd ) ,
181+ )
163182 }
164183 }
165184 }
@@ -171,13 +190,14 @@ export const WarpPlugin: Plugin = async ({ client, directory }) => {
171190 // completion notification.)
172191 "chat.message" : async ( input , output ) => {
173192 const cwd = directory || ""
193+
174194 const queryText = extractTextFromParts ( output . parts )
175195 if ( ! queryText ) return
176196
177197 const body = buildPayload ( "prompt_submit" , input . sessionID , cwd , {
178198 query : truncate ( queryText , 200 ) ,
179199 } )
180- warpNotify ( NOTIFICATION_TITLE , body )
200+ await maybeWarpNotify ( input . sessionID , body )
181201 } ,
182202
183203 // Fires before a tool executes — used to detect the built-in
@@ -189,7 +209,7 @@ export const WarpPlugin: Plugin = async ({ client, directory }) => {
189209 const body = buildPayload ( "question_asked" , input . sessionID , cwd , {
190210 tool_name : input . tool ,
191211 } )
192- warpNotify ( NOTIFICATION_TITLE , body )
212+ await maybeWarpNotify ( input . sessionID , body )
193213 } ,
194214
195215 // Tool completion — fires after every tool call
@@ -201,7 +221,7 @@ export const WarpPlugin: Plugin = async ({ client, directory }) => {
201221 const body = buildPayload ( "tool_complete" , sessionId , cwd , {
202222 tool_name : toolName ,
203223 } )
204- warpNotify ( NOTIFICATION_TITLE , body )
224+ await maybeWarpNotify ( sessionId , body )
205225 } ,
206226 }
207227}
0 commit comments