6
6
OpenHandsObservation ,
7
7
CommandObservation ,
8
8
IPythonObservation ,
9
+ RecallObservation ,
9
10
} from "#/types/core/observations" ;
10
11
import { OpenHandsAction } from "#/types/core/actions" ;
11
12
import { OpenHandsEventType } from "#/types/core/base" ;
@@ -22,6 +23,7 @@ const HANDLED_ACTIONS: OpenHandsEventType[] = [
22
23
"browse" ,
23
24
"browse_interactive" ,
24
25
"edit" ,
26
+ "recall" ,
25
27
] ;
26
28
27
29
function getRiskText ( risk : ActionSecurityRisk ) {
@@ -112,6 +114,9 @@ export const chatSlice = createSlice({
112
114
} else if ( actionID === "browse_interactive" ) {
113
115
// Include the browser_actions in the content
114
116
text = `**Action:**\n\n\`\`\`python\n${ action . payload . args . browser_actions } \n\`\`\`` ;
117
+ } else if ( actionID === "recall" ) {
118
+ // skip recall actions
119
+ return ;
115
120
}
116
121
if ( actionID === "run" || actionID === "run_ipython" ) {
117
122
if (
@@ -143,6 +148,73 @@ export const chatSlice = createSlice({
143
148
if ( ! HANDLED_ACTIONS . includes ( observationID ) ) {
144
149
return ;
145
150
}
151
+
152
+ // Special handling for RecallObservation - create a new message instead of updating an existing one
153
+ if ( observationID === "recall" ) {
154
+ const recallObs = observation . payload as RecallObservation ;
155
+ let content = `` ;
156
+
157
+ // Handle workspace context
158
+ if ( recallObs . extras . recall_type === "workspace_context" ) {
159
+ if ( recallObs . extras . repo_name ) {
160
+ content += `\n\n**Repository:** ${ recallObs . extras . repo_name } ` ;
161
+ }
162
+ if ( recallObs . extras . repo_directory ) {
163
+ content += `\n\n**Directory:** ${ recallObs . extras . repo_directory } ` ;
164
+ }
165
+ if ( recallObs . extras . date ) {
166
+ content += `\n\n**Date:** ${ recallObs . extras . date } ` ;
167
+ }
168
+ if (
169
+ recallObs . extras . runtime_hosts &&
170
+ Object . keys ( recallObs . extras . runtime_hosts ) . length > 0
171
+ ) {
172
+ content += `\n\n**Available Hosts**` ;
173
+ for ( const [ host , port ] of Object . entries (
174
+ recallObs . extras . runtime_hosts ,
175
+ ) ) {
176
+ content += `\n\n- ${ host } (port ${ port } )` ;
177
+ }
178
+ }
179
+ if ( recallObs . extras . repo_instructions ) {
180
+ content += `\n\n**Repository Instructions:**\n\n${ recallObs . extras . repo_instructions } ` ;
181
+ }
182
+ if ( recallObs . extras . additional_agent_instructions ) {
183
+ content += `\n\n**Additional Instructions:**\n\n${ recallObs . extras . additional_agent_instructions } ` ;
184
+ }
185
+ }
186
+
187
+ // Create a new message for the observation
188
+ // Use the correct translation ID format that matches what's in the i18n file
189
+ const translationID = `OBSERVATION_MESSAGE$${ observationID . toUpperCase ( ) } ` ;
190
+
191
+ // Handle microagent knowledge
192
+ if (
193
+ recallObs . extras . microagent_knowledge &&
194
+ recallObs . extras . microagent_knowledge . length > 0
195
+ ) {
196
+ content += `\n\n**Triggered Microagent Knowledge:**` ;
197
+ for ( const knowledge of recallObs . extras . microagent_knowledge ) {
198
+ content += `\n\n- **${ knowledge . name } ** (triggered by keyword: ${ knowledge . trigger } )\n\n\`\`\`\n${ knowledge . content } \n\`\`\`` ;
199
+ }
200
+ }
201
+
202
+ const message : Message = {
203
+ type : "action" ,
204
+ sender : "assistant" ,
205
+ translationID,
206
+ eventID : observation . payload . id ,
207
+ content,
208
+ imageUrls : [ ] ,
209
+ timestamp : new Date ( ) . toISOString ( ) ,
210
+ success : true ,
211
+ } ;
212
+
213
+ state . messages . push ( message ) ;
214
+ return ; // Skip the normal observation handling below
215
+ }
216
+
217
+ // Normal handling for other observation types
146
218
const translationID = `OBSERVATION_MESSAGE$${ observationID . toUpperCase ( ) } ` ;
147
219
const causeID = observation . payload . cause ;
148
220
const causeMessage = state . messages . find (
0 commit comments