Skip to content

Commit 4efbb28

Browse files
authored
Auto-attach recently viewed charms and add listRecent() tool (#1986)
* Auto-attach currently viewed charm and introduce `listRecent` tool * Make system prompt customizable for `chatbot.tsx`
1 parent 8f868c0 commit 4efbb28

File tree

2 files changed

+79
-10
lines changed

2 files changed

+79
-10
lines changed

packages/patterns/chatbot.tsx

Lines changed: 77 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -122,6 +122,7 @@ type ChatInput = {
122122
messages: Default<Array<BuiltInLLMMessage>, []>;
123123
tools: any;
124124
theme?: any;
125+
system?: string;
125126
};
126127

127128
type PromptAttachment = {
@@ -268,15 +269,71 @@ const listMentionable = handler<
268269
},
269270
);
270271

272+
const listRecent = handler<
273+
{
274+
/** A cell to store the result text */
275+
result: Cell<string>;
276+
},
277+
{ recentCharms: MentionableCharm[] }
278+
>(
279+
(args, state) => {
280+
try {
281+
const namesList = state.recentCharms.map((charm) => charm[NAME]);
282+
args.result.set(JSON.stringify(namesList));
283+
} catch (error) {
284+
args.result.set(`Error: ${(error as any)?.message || "<error>"}`);
285+
}
286+
},
287+
);
288+
271289
export default recipe<ChatInput, ChatOutput>(
272290
"Chat",
273-
({ messages, tools, theme }) => {
291+
({ messages, tools, theme, system }) => {
274292
const model = cell<string>("anthropic:claude-sonnet-4-5");
275293
const allAttachments = cell<Array<PromptAttachment>>([]);
276294
const mentionable = schemaifyWish<MentionableCharm[]>("#mentionable");
295+
const recentCharms = schemaifyWish<MentionableCharm[]>("#recent");
296+
297+
// Auto-attach the most recent charm (union with user attachments)
298+
const attachmentsWithRecent = derive(
299+
[allAttachments, recentCharms],
300+
([userAttachments, recent]: [
301+
Array<PromptAttachment>,
302+
Array<MentionableCharm>,
303+
]): Array<PromptAttachment> => {
304+
const attachments = userAttachments || [];
305+
306+
// If there's a most recent charm, auto-inject it
307+
if (recent && recent.length > 0) {
308+
const mostRecent = recent[0];
309+
const mostRecentName = mostRecent[NAME];
310+
311+
// Check if it's already in the attachments
312+
const alreadyAttached = attachments.some(
313+
(a) => a.type === "mention" && a.name === mostRecentName,
314+
);
315+
316+
if (!alreadyAttached && mostRecentName) {
317+
// Add the most recent charm to the beginning
318+
const id = `attachment-auto-recent-${mostRecentName}`;
319+
return [
320+
{
321+
id,
322+
name: mostRecentName,
323+
type: "mention" as const,
324+
charm: mostRecent,
325+
},
326+
...attachments,
327+
];
328+
}
329+
}
277330

278-
// Derive tools from attachments
279-
const dynamicTools = derive(allAttachments, (attachments) => {
331+
return attachments;
332+
},
333+
);
334+
335+
// Derive tools from attachments (including auto-attached recent charm)
336+
const dynamicTools = derive(attachmentsWithRecent, (attachments) => {
280337
const tools: Record<string, any> = {};
281338

282339
for (const attachment of attachments || []) {
@@ -296,16 +353,22 @@ export default recipe<ChatInput, ChatOutput>(
296353
navigateToAttachment: {
297354
description:
298355
"Navigate to a mentionable by its ID in the attachments array.",
299-
handler: navigateToAttachment({ allAttachments }),
356+
handler: navigateToAttachment({
357+
allAttachments: attachmentsWithRecent,
358+
}),
300359
},
301360
listAttachments: {
302361
description: "List all attachments in the attachments array.",
303-
handler: listAttachments({ allAttachments }),
362+
handler: listAttachments({ allAttachments: attachmentsWithRecent }),
304363
},
305364
listMentionable: {
306365
description: "List all mentionable NAMEs in the space.",
307366
handler: listMentionable({ mentionable }),
308367
},
368+
listRecent: {
369+
description: "List all recently viewed charms in the space.",
370+
handler: listRecent({ recentCharms }),
371+
},
309372
addAttachment: {
310373
description:
311374
"Add a new attachment to the attachments array by its mentionable NAME.",
@@ -330,8 +393,9 @@ export default recipe<ChatInput, ChatOutput>(
330393

331394
const { addMessage, cancelGeneration, pending, flattenedTools } = llmDialog(
332395
{
333-
system:
334-
"You are a polite but efficient assistant. Think Star Trek computer - helpful and professional without unnecessary conversation. Let your actions speak for themselves.\n\nTool usage priority:\n- Search this space first: listMentionable → addAttachment to access items\n- Search externally only when clearly needed: searchWeb for current events, external information, or when nothing relevant exists in the space\n\nBe matter-of-fact. Prefer action to explanation.",
396+
system: derive(system, (s) =>
397+
s ??
398+
"You are a polite but efficient assistant."),
335399
messages,
336400
tools: mergedTools,
337401
model,
@@ -362,7 +426,10 @@ export default recipe<ChatInput, ChatOutput>(
362426
$mentionable={mentionable}
363427
modelItems={items}
364428
$model={model}
365-
onct-send={sendMessage({ addMessage, allAttachments })}
429+
onct-send={sendMessage({
430+
addMessage,
431+
allAttachments: attachmentsWithRecent,
432+
})}
366433
onct-stop={cancelGeneration}
367434
onct-attachment-add={addAttachment({ allAttachments })}
368435
onct-attachment-remove={removeAttachment({ allAttachments })}
@@ -389,7 +456,7 @@ export default recipe<ChatInput, ChatOutput>(
389456
const attachmentsAndTools = (
390457
<ct-hstack align="center" gap="1">
391458
<ct-attachments-bar
392-
attachments={allAttachments}
459+
attachments={attachmentsWithRecent}
393460
removable
394461
onct-remove={removeAttachment({ allAttachments })}
395462
/>
@@ -431,7 +498,7 @@ export default recipe<ChatInput, ChatOutput>(
431498
}),
432499
cancelGeneration,
433500
title,
434-
attachments: allAttachments,
501+
attachments: attachmentsWithRecent,
435502
tools: flattenedTools,
436503
ui: {
437504
chatLog,

packages/patterns/omnibox-fab.tsx

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -39,6 +39,8 @@ export default recipe<OmniboxFABInput>(
3939
"OmniboxFAB",
4040
({ mentionable: _mentionable }) => {
4141
const omnibot = Chatbot({
42+
system:
43+
"You are a polite but efficient assistant. Think Star Trek computer - helpful and professional without unnecessary conversation. Let your actions speak for themselves.\n\nTool usage priority:\n- Search this space first: listMentionable → addAttachment to access items\n- Search externally only when clearly needed: searchWeb for current events, external information, or when nothing relevant exists in the space\n\nBe matter-of-fact. Prefer action to explanation.",
4244
messages: [],
4345
tools: {
4446
searchWeb: {

0 commit comments

Comments
 (0)