This repository has no description
0

Configure Feed

Select the types of activity you want to include in your feed.

Fix island:analyze: pass tools and permissionMode to createSession

Tools were only registered on the ephemeral createAgent session
(which gets closed immediately). The working createSession needs
tools + bypassPermissions too, otherwise the agent cant see or
call island_save.

Also simplified tool schema to flat strings (parsed on execute)
since nested array-of-objects schemas may not register reliably.

👾 Generated with [Letta Code](https://letta.com)

Co-Authored-By: Letta Code <noreply@letta.com>

author
nandi
co-author
Letta Code
date (May 15, 2026, 6:24 AM UTC) commit ccebf5c6 parent 03409f52
+42 -47
+42 -47
src/island-analyze.ts
··· 186 186 const islandSaveTool: AnyAgentTool = { 187 187 label: 'island_save', 188 188 name: 'island_save', 189 - description: `Save your analysis of this island. Call once when you have completed your analysis. 190 - 191 - Fields: 192 - - title: concise sentence capturing the island's core narrative (max 300 chars) 193 - - themes: list of key themes (max 20, each max 100 chars) 194 - - highlights: list of vertex URLs or edge descriptions that are semantically significant (max 20) 195 - - tensions: contradictions or unresolved tensions within the island (max 10, each max 500 chars) 196 - - openQuestions: questions raised by this island (max 10, each max 500 chars) 197 - - synthesis: prose synthesis connecting the island's vertices and edges into a coherent narrative (max 20000 chars) 198 - - newConnections: suggested new edges between vertices, with source URL, target URL, connectionType, and a note explaining why (max 50)`, 189 + description: `Save your analysis of this island. Call this tool once when you have completed your analysis. This is the ONLY way to save your work — you MUST call this tool.`, 199 190 parameters: { 200 191 type: 'object', 201 192 properties: { 202 193 title: { type: 'string', description: 'Concise sentence capturing the core narrative' }, 203 - themes: { type: 'array', items: { type: 'string' }, description: 'Key themes' }, 204 - highlights: { type: 'array', items: { type: 'string' }, description: 'Semantically significant vertices or edges' }, 205 - tensions: { type: 'array', items: { type: 'string' }, description: 'Contradictions or unresolved tensions' }, 206 - openQuestions: { type: 'array', items: { type: 'string' }, description: 'Questions raised by this island' }, 194 + themes: { type: 'string', description: 'Comma-separated key themes' }, 195 + highlights: { type: 'string', description: 'Comma-separated semantically significant vertices or edges' }, 196 + tensions: { type: 'string', description: 'Comma-separated contradictions or unresolved tensions' }, 197 + openQuestions: { type: 'string', description: 'Comma-separated questions raised by this island' }, 207 198 synthesis: { type: 'string', description: 'Prose synthesis of the island' }, 208 - newConnections: { 209 - type: 'array', 210 - items: { 211 - type: 'object', 212 - properties: { 213 - source: { type: 'string', description: 'Source vertex URL' }, 214 - target: { type: 'string', description: 'Target vertex URL' }, 215 - connectionType: { type: 'string', description: 'Relationship type (e.g. related-to, supports, contradicts)' }, 216 - note: { type: 'string', description: 'Why this connection exists' }, 217 - }, 218 - required: ['source', 'target', 'connectionType', 'note'], 219 - }, 220 - description: 'Suggested new edges between vertices', 221 - }, 199 + newConnections: { type: 'string', description: 'Semicolon-separated new edges as: source|target|type|note' }, 222 200 }, 223 201 required: ['title', 'themes', 'synthesis'], 224 202 }, 225 203 execute: async (_id, args): Promise<AgentToolResult<unknown>> => { 226 - const a = args as Record<string, unknown>; 204 + const a = args as Record<string, string>; 205 + const parseList = (s: string | undefined) => (s ? s.split(',').map(x => x.trim()).filter(Boolean) : []); 206 + const parseConnections = (s: string | undefined) => { 207 + if (!s) return []; 208 + return s.split(';').map(x => x.trim()).filter(Boolean).map(part => { 209 + const [source, target, connectionType, ...noteParts] = part.split('|').map(x => x.trim()); 210 + return { source: source ?? '', target: target ?? '', connectionType: connectionType ?? 'related-to', note: noteParts.join('|') || '' }; 211 + }); 212 + }; 227 213 pendingResult = { 228 214 islandId: '', 229 215 lexmin: '', 230 - title: String(a.title ?? ''), 231 - themes: (a.themes as string[]) ?? [], 232 - highlights: (a.highlights as string[]) ?? [], 233 - tensions: (a.tensions as string[]) ?? [], 234 - openQuestions: (a.openQuestions as string[]) ?? [], 235 - synthesis: String(a.synthesis ?? ''), 236 - newConnections: (a.newConnections as Array<{ source: string; target: string; connectionType: string; note: string }>) ?? [], 216 + title: a.title ?? '', 217 + themes: parseList(a.themes), 218 + highlights: parseList(a.highlights), 219 + tensions: parseList(a.tensions), 220 + openQuestions: parseList(a.openQuestions), 221 + synthesis: a.synthesis ?? '', 222 + newConnections: parseConnections(a.newConnections), 237 223 analyzedAt: new Date().toISOString(), 238 224 }; 239 225 return { ··· 252 238 const minSizeArg = args.find((a) => a.startsWith('--min-size=')); 253 239 const minSize = minSizeArg ? Number(minSizeArg.split('=')[1]) : 3; 254 240 const modelArg = args.find((a) => a.startsWith('--model=')); 255 - const model = modelArg ? modelArg.split('=')[1] : 'auto-fast'; 241 + const model = modelArg ? modelArg.split('=')[1] : undefined; 256 242 257 243 const targetId = args.find((a) => !a.startsWith('--')); 258 244 ··· 297 283 model, 298 284 persona: `You are a research analyst that synthesizes connected components (islands) from a knowledge graph into structured analysis records. 299 285 300 - Given an island — a cluster of URLs connected by semantic edges — you produce: 301 - 1. A title that captures the island's core narrative in one sentence 302 - 2. Themes that describe what this cluster is about 303 - 3. Highlights — vertices or edges that are semantically significant 304 - 4. Tensions — contradictions or unresolved questions within the cluster 305 - 5. Open questions raised by this island 306 - 6. A synthesis — a prose narrative connecting the vertices and edges into a coherent story 307 - 7. New connections — suggested edges between vertices that would strengthen the island 286 + Given an island — a cluster of URLs connected by semantic edges — you MUST call the island_save tool with your analysis. Do NOT just write text — you MUST use the island_save tool call. 308 287 309 - You always call island_save once when done. You do not ask for permission.`, 288 + Your output must be a single island_save tool call with these fields: 289 + - title: concise sentence capturing the island's core narrative 290 + - themes: list of key themes 291 + - highlights: semantically significant vertices or edges 292 + - tensions: contradictions or unresolved tensions 293 + - openQuestions: questions raised by this island 294 + - synthesis: prose narrative connecting the vertices and edges 295 + - newConnections: suggested new edges between vertices 296 + 297 + CRITICAL: You MUST call island_save. Do not just describe your analysis in text. Call the tool.`, 310 298 human: 'island-analyzer', 311 299 tools: [islandSaveTool], 312 - allowedTools: ['island_save'], 313 300 permissionMode: 'bypassPermissions', 314 301 memfs: false, 315 302 systemInfoReminder: false, ··· 319 306 320 307 const session = createSession(agentId, { 321 308 memfsStartup: 'skip', 309 + tools: [islandSaveTool], 310 + permissionMode: 'bypassPermissions', 322 311 }); 323 312 324 313 await session.initialize(); ··· 351 340 352 341 const result: SDKResultMessage = await session.runTurn(prompt); 353 342 343 + console.error(` Result: success=${result.success}, stopReason=${result.stopReason ?? 'none'}, duration=${result.durationMs}ms`); 344 + 354 345 if (!result.success) { 355 346 console.error(` Agent failed: ${result.error ?? result.errorCode ?? 'unknown'}`); 356 347 continue; ··· 358 349 359 350 if (!pendingResult) { 360 351 console.error(` Agent did not call island_save`); 352 + // Log what the agent produced 353 + if (result.result) { 354 + console.error(` Agent output: ${result.result.slice(0, 500)}`); 355 + } 361 356 continue; 362 357 } 363 358