Skip to content

Commit e28dc4c

Browse files
committed
refactor(agents): deduplicate code and add documentation
- Extract shared helper functions in file-picker.ts to module level: extractSpawnResults(), extractLastMessageText(), extractErrorMessage() - Add JSDoc comments to all extracted functions - Add clarifying comments in context-pruner.ts explaining why summarizeToolCall is intentionally duplicated for serialization
1 parent 5cad4be commit e28dc4c

File tree

2 files changed

+200
-69
lines changed

2 files changed

+200
-69
lines changed

agents/context-pruner.ts

Lines changed: 2 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -70,11 +70,7 @@ export function getTextContent(message: Message): string {
7070

7171
/**
7272
* Summarizes a tool call into a human-readable description.
73-
* Handles various tool types with appropriate formatting.
74-
*
75-
* @param toolName - The name of the tool
76-
* @param input - The tool's input parameters
77-
* @returns A concise summary of the tool call
73+
* DUPLICATE: Keep in sync with the copy inside handleSteps (required for serialization).
7874
*/
7975
export function summarizeToolCall(
8076
toolName: string,
@@ -369,7 +365,7 @@ const definition: AgentDefinition = {
369365
}
370366

371367
/**
372-
* Summarizes a tool call into a human-readable description.
368+
* DUPLICATE: Keep in sync with module-level summarizeToolCall (required for serialization).
373369
*/
374370
function summarizeToolCall(
375371
toolName: string,

agents/file-explorer/file-picker.ts

Lines changed: 198 additions & 63 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,86 @@ import type { StepText, ToolCall } from '../types/agent-definition'
99

1010
type FilePickerMode = 'default' | 'max'
1111

12+
/**
13+
* Type guard to check if value is a non-null object.
14+
* DUPLICATE: Keep in sync with copies inside handleStepsDefault and handleStepsMax (required for serialization).
15+
*/
16+
function isObject(value: unknown): value is Record<string, unknown> {
17+
return value !== null && typeof value === 'object'
18+
}
19+
20+
/**
21+
* Extracts spawn results from tool result array, returning agent values.
22+
* DUPLICATE: Keep in sync with copies inside handleStepsDefault and handleStepsMax (required for serialization).
23+
*/
24+
function extractSpawnResults(results: unknown[] | undefined): unknown[] {
25+
if (!results || results.length === 0) return []
26+
const jsonResult = results.find(
27+
(r): r is { type: 'json'; value: unknown } =>
28+
isObject(r) && r.type === 'json',
29+
)
30+
if (!jsonResult?.value) return []
31+
const spawnedResults = Array.isArray(jsonResult.value)
32+
? jsonResult.value
33+
: [jsonResult.value]
34+
// Each spawned result may be an object with a .value property (spawn wrapper)
35+
// or the agent output directly (type: 'lastMessage' or type: 'error')
36+
return spawnedResults
37+
.map((result: unknown) => {
38+
if (!isObject(result)) return undefined
39+
// If it's a spawn wrapper with .value, extract the value
40+
if ('value' in result && result.type !== 'lastMessage' && result.type !== 'error') {
41+
return result.value
42+
}
43+
// Otherwise it's the agent output directly
44+
return result
45+
})
46+
.filter(Boolean)
47+
}
48+
49+
/**
50+
* Extracts the most recent assistant text from an agent's output.
51+
* DUPLICATE: Keep in sync with copies inside handleStepsDefault and handleStepsMax (required for serialization).
52+
*/
53+
function extractLastMessageText(agentOutput: unknown): string | null {
54+
if (!isObject(agentOutput)) return null
55+
if (agentOutput.type !== 'lastMessage' || !Array.isArray(agentOutput.value)) {
56+
return null
57+
}
58+
for (let i = agentOutput.value.length - 1; i >= 0; i--) {
59+
const message = agentOutput.value[i]
60+
if (
61+
isObject(message) &&
62+
message.role === 'assistant' &&
63+
Array.isArray(message.content)
64+
) {
65+
for (const part of message.content) {
66+
if (
67+
isObject(part) &&
68+
part.type === 'text' &&
69+
typeof part.text === 'string'
70+
) {
71+
return part.text
72+
}
73+
}
74+
}
75+
}
76+
return null
77+
}
78+
79+
/**
80+
* Extracts error message from agent output if present.
81+
* DUPLICATE: Keep in sync with copies inside handleStepsDefault and handleStepsMax (required for serialization).
82+
*/
83+
function extractErrorMessage(agentOutput: unknown): string | null {
84+
if (!isObject(agentOutput)) return null
85+
if (agentOutput.type === 'error') {
86+
if (typeof agentOutput.message === 'string') return agentOutput.message
87+
if (typeof agentOutput.value === 'string') return agentOutput.value
88+
}
89+
return null
90+
}
91+
1292
export const createFilePicker = (
1393
mode: FilePickerMode,
1494
): Omit<SecretAgentDefinition, 'id'> => {
@@ -67,6 +147,71 @@ const handleStepsDefault: SecretAgentDefinition['handleSteps'] = function* ({
67147
prompt,
68148
params,
69149
}) {
150+
// ============================================================================
151+
// Helper functions duplicated inside generator for sandbox serialization.
152+
// DUPLICATE: Keep in sync with module-level versions.
153+
// ============================================================================
154+
function isObject(value: unknown): value is Record<string, unknown> {
155+
return value !== null && typeof value === 'object'
156+
}
157+
158+
function extractSpawnResults(results: unknown[] | undefined): unknown[] {
159+
if (!results || results.length === 0) return []
160+
const jsonResult = results.find(
161+
(r): r is { type: 'json'; value: unknown } =>
162+
isObject(r) && r.type === 'json',
163+
)
164+
if (!jsonResult?.value) return []
165+
const spawnedResults = Array.isArray(jsonResult.value)
166+
? jsonResult.value
167+
: [jsonResult.value]
168+
return spawnedResults
169+
.map((result: unknown) => {
170+
if (!isObject(result)) return undefined
171+
if ('value' in result && result.type !== 'lastMessage' && result.type !== 'error') {
172+
return result.value
173+
}
174+
return result
175+
})
176+
.filter(Boolean)
177+
}
178+
179+
function extractLastMessageText(agentOutput: unknown): string | null {
180+
if (!isObject(agentOutput)) return null
181+
if (agentOutput.type !== 'lastMessage' || !Array.isArray(agentOutput.value)) {
182+
return null
183+
}
184+
for (let i = agentOutput.value.length - 1; i >= 0; i--) {
185+
const message = agentOutput.value[i]
186+
if (
187+
isObject(message) &&
188+
message.role === 'assistant' &&
189+
Array.isArray(message.content)
190+
) {
191+
for (const part of message.content) {
192+
if (
193+
isObject(part) &&
194+
part.type === 'text' &&
195+
typeof part.text === 'string'
196+
) {
197+
return part.text
198+
}
199+
}
200+
}
201+
}
202+
return null
203+
}
204+
205+
function extractErrorMessage(agentOutput: unknown): string | null {
206+
if (!isObject(agentOutput)) return null
207+
if (agentOutput.type === 'error') {
208+
if (typeof agentOutput.message === 'string') return agentOutput.message
209+
if (typeof agentOutput.value === 'string') return agentOutput.value
210+
}
211+
return null
212+
}
213+
// ============================================================================
214+
70215
const { toolResult: fileListerResults } = yield {
71216
toolName: 'spawn_agents',
72217
input: {
@@ -120,50 +265,78 @@ const handleStepsDefault: SecretAgentDefinition['handleSteps'] = function* ({
120265

121266
yield 'STEP'
122267

123-
function extractSpawnResults(results: any[] | undefined): any[] {
268+
}
269+
270+
// handleSteps for max mode - spawns 2 file-listers in parallel
271+
const handleStepsMax: SecretAgentDefinition['handleSteps'] = function* ({
272+
prompt,
273+
params,
274+
}) {
275+
// ============================================================================
276+
// Helper functions duplicated inside generator for sandbox serialization.
277+
// DUPLICATE: Keep in sync with module-level versions.
278+
// ============================================================================
279+
function isObject(value: unknown): value is Record<string, unknown> {
280+
return value !== null && typeof value === 'object'
281+
}
282+
283+
function extractSpawnResults(results: unknown[] | undefined): unknown[] {
124284
if (!results || results.length === 0) return []
125-
const jsonResult = results.find((r) => r.type === 'json')
285+
const jsonResult = results.find(
286+
(r): r is { type: 'json'; value: unknown } =>
287+
isObject(r) && r.type === 'json',
288+
)
126289
if (!jsonResult?.value) return []
127290
const spawnedResults = Array.isArray(jsonResult.value)
128291
? jsonResult.value
129292
: [jsonResult.value]
130-
return spawnedResults.map((result: any) => result?.value).filter(Boolean)
293+
return spawnedResults
294+
.map((result: unknown) => {
295+
if (!isObject(result)) return undefined
296+
if ('value' in result && result.type !== 'lastMessage' && result.type !== 'error') {
297+
return result.value
298+
}
299+
return result
300+
})
301+
.filter(Boolean)
131302
}
132303

133-
function extractLastMessageText(agentOutput: any): string | null {
134-
if (!agentOutput) return null
135-
if (
136-
agentOutput.type === 'lastMessage' &&
137-
Array.isArray(agentOutput.value)
138-
) {
139-
for (let i = agentOutput.value.length - 1; i >= 0; i--) {
140-
const message = agentOutput.value[i]
141-
if (message.role === 'assistant' && Array.isArray(message.content)) {
142-
for (const part of message.content) {
143-
if (part.type === 'text' && typeof part.text === 'string') {
144-
return part.text
145-
}
304+
function extractLastMessageText(agentOutput: unknown): string | null {
305+
if (!isObject(agentOutput)) return null
306+
if (agentOutput.type !== 'lastMessage' || !Array.isArray(agentOutput.value)) {
307+
return null
308+
}
309+
for (let i = agentOutput.value.length - 1; i >= 0; i--) {
310+
const message = agentOutput.value[i]
311+
if (
312+
isObject(message) &&
313+
message.role === 'assistant' &&
314+
Array.isArray(message.content)
315+
) {
316+
for (const part of message.content) {
317+
if (
318+
isObject(part) &&
319+
part.type === 'text' &&
320+
typeof part.text === 'string'
321+
) {
322+
return part.text
146323
}
147324
}
148325
}
149326
}
150327
return null
151328
}
152329

153-
function extractErrorMessage(agentOutput: any): string | null {
154-
if (!agentOutput) return null
330+
function extractErrorMessage(agentOutput: unknown): string | null {
331+
if (!isObject(agentOutput)) return null
155332
if (agentOutput.type === 'error') {
156-
return agentOutput.message ?? agentOutput.value ?? null
333+
if (typeof agentOutput.message === 'string') return agentOutput.message
334+
if (typeof agentOutput.value === 'string') return agentOutput.value
157335
}
158336
return null
159337
}
160-
}
338+
// ============================================================================
161339

162-
// handleSteps for max mode - spawns 2 file-listers in parallel
163-
const handleStepsMax: SecretAgentDefinition['handleSteps'] = function* ({
164-
prompt,
165-
params,
166-
}) {
167340
const { toolResult: fileListerResults } = yield {
168341
toolName: 'spawn_agents',
169342
input: {
@@ -221,44 +394,6 @@ const handleStepsMax: SecretAgentDefinition['handleSteps'] = function* ({
221394
}
222395

223396
yield 'STEP'
224-
225-
function extractSpawnResults(results: any[] | undefined): any[] {
226-
if (!results || results.length === 0) return []
227-
const jsonResult = results.find((r) => r.type === 'json')
228-
if (!jsonResult?.value) return []
229-
const spawnedResults = Array.isArray(jsonResult.value)
230-
? jsonResult.value
231-
: [jsonResult.value]
232-
return spawnedResults.map((result: any) => result?.value).filter(Boolean)
233-
}
234-
235-
function extractLastMessageText(agentOutput: any): string | null {
236-
if (!agentOutput) return null
237-
if (
238-
agentOutput.type === 'lastMessage' &&
239-
Array.isArray(agentOutput.value)
240-
) {
241-
for (let i = agentOutput.value.length - 1; i >= 0; i--) {
242-
const message = agentOutput.value[i]
243-
if (message.role === 'assistant' && Array.isArray(message.content)) {
244-
for (const part of message.content) {
245-
if (part.type === 'text' && typeof part.text === 'string') {
246-
return part.text
247-
}
248-
}
249-
}
250-
}
251-
}
252-
return null
253-
}
254-
255-
function extractErrorMessage(agentOutput: any): string | null {
256-
if (!agentOutput) return null
257-
if (agentOutput.type === 'error') {
258-
return agentOutput.message ?? agentOutput.value ?? null
259-
}
260-
return null
261-
}
262397
}
263398

264399
const definition: SecretAgentDefinition = {

0 commit comments

Comments
 (0)