@@ -9,11 +9,10 @@ import { truncate, extractTextFromParts } from "./utils"
99// NOTE: do not `export` this constant — opencode's legacy plugin loader
1010// treats every named export as a plugin function and throws if any export
1111// is not a function ("Plugin export is not a function").
12- const PLUGIN_VERSION = "0.1.6 "
12+ const 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,17 +71,41 @@ 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
@@ -129,28 +151,34 @@ export const WarpPlugin: Plugin = async ({ client, directory }) => {
129151 response : truncate ( response , 200 ) ,
130152 transcript_path : "" ,
131153 } )
132- warpNotify ( NOTIFICATION_TITLE , body )
154+ await maybeWarpNotify ( sessionId , body )
133155 return
134156 }
135157
136158 case "permission.updated" : {
137- sendPermissionNotification ( event . properties , cwd )
159+ await maybeWarpNotify (
160+ event . properties . sessionID ,
161+ buildPermissionPayload ( event . properties , cwd ) ,
162+ )
138163 return
139164 }
140165
141166 case "permission.replied" : {
142167 const { sessionID, response } = event . properties
143168 if ( response === "reject" ) return
144169 const body = buildPayload ( "permission_replied" , sessionID , cwd )
145- warpNotify ( NOTIFICATION_TITLE , body )
170+ await maybeWarpNotify ( sessionID , body )
146171 return
147172 }
148173
149174 default : {
150175 // permission.asked is listed in the opencode docs but has no SDK type.
151176 // Handle it with the same logic as permission.updated.
152177 if ( ( event as any ) . type === "permission.asked" ) {
153- sendPermissionNotification ( ( event as any ) . properties , cwd )
178+ await maybeWarpNotify (
179+ ( event as any ) . properties ?. sessionID ,
180+ buildPermissionPayload ( ( event as any ) . properties , cwd ) ,
181+ )
154182 }
155183 }
156184 }
@@ -162,13 +190,14 @@ export const WarpPlugin: Plugin = async ({ client, directory }) => {
162190 // completion notification.)
163191 "chat.message" : async ( input , output ) => {
164192 const cwd = directory || ""
193+
165194 const queryText = extractTextFromParts ( output . parts )
166195 if ( ! queryText ) return
167196
168197 const body = buildPayload ( "prompt_submit" , input . sessionID , cwd , {
169198 query : truncate ( queryText , 200 ) ,
170199 } )
171- warpNotify ( NOTIFICATION_TITLE , body )
200+ await maybeWarpNotify ( input . sessionID , body )
172201 } ,
173202
174203 // Fires before a tool executes — used to detect the built-in
@@ -180,7 +209,7 @@ export const WarpPlugin: Plugin = async ({ client, directory }) => {
180209 const body = buildPayload ( "question_asked" , input . sessionID , cwd , {
181210 tool_name : input . tool ,
182211 } )
183- warpNotify ( NOTIFICATION_TITLE , body )
212+ await maybeWarpNotify ( input . sessionID , body )
184213 } ,
185214
186215 // Tool completion — fires after every tool call
@@ -192,7 +221,7 @@ export const WarpPlugin: Plugin = async ({ client, directory }) => {
192221 const body = buildPayload ( "tool_complete" , sessionId , cwd , {
193222 tool_name : toolName ,
194223 } )
195- warpNotify ( NOTIFICATION_TITLE , body )
224+ await maybeWarpNotify ( sessionId , body )
196225 } ,
197226 }
198227}
0 commit comments