Compare commits

..

1 Commits
1.4.2 ... 1.4.3

Author SHA1 Message Date
3f1289d993 Small agent tweaks
All checks were successful
Publish Library / Build NPM Project (push) Successful in 49s
Publish Library / Tag Version (push) Successful in 9s
2026-08-04 14:33:28 -04:00
3 changed files with 22 additions and 7 deletions

View File

@@ -1,6 +1,6 @@
{
"name": "@ztimson/ai-utils",
"version": "1.4.2",
"version": "1.4.3",
"description": "AI Utility library",
"author": "Zak Timson",
"license": "MIT",

View File

@@ -132,8 +132,8 @@ class LLM {
return {
name: toolName,
description: `${a.delegate ? 'Delegate to ' : ''}Subagent: ${a.description || a.name}`,
args: <any>(a.delegate ? {} : {
context: {type: 'string', description: 'Summary of related messages, samples, files, etc...', required: true},
args: <any>({
context: !a.delegate ? {type: 'string', description: 'Summary of related messages, samples, files, etc...', required: true} : undefined,
instructions: {type: 'string', description: 'Detailed instructions for subagent to complete', required: true},
}),
fn: async (args: any, stream: any, ai: any, id?: string) => {
@@ -148,8 +148,9 @@ class LLM {
const q = a.delegate ? '' : `${args.instructions}${args.context ? `\n\n<context>${args.context}</context>` : ''}`;
const request = this.ask(q, {
system: `You are a specialized subagent. ${a.delegate ? 'Your output streams directly to the user for the remainder of this turn. You are mid conversation - dispense with greetings.' : 'You are wrapped in a tool call that will be analysis by an LLM - dispense with conversation'}
As a subagent, focus on executing your task completely using available tools and returning only the final result - no commentary, questions, or dialogue.
system: `You are a specialized subagent being called from an orchestrator
${a.delegate ? 'Your output streams directly to the user for the remainder of this turn. You are mid conversation' : 'You are wrapped in a tool call that will be analysis by an LLM'}
Dispense with greetings and focus on your instructions using available tools and returning only the final result unless specifically instructed to converse
${a.system}`,
model: a.model || undefined,

View File

@@ -186,6 +186,17 @@ export class MemoryManager {
constructor(private llm: any) {}
private ghostNodes(memories: Memory[]): string[] {
const names = new Set(memories.map(m => m.name));
const ghosts = new Set<string>();
for (const m of memories) {
for (const link of m.links) {
if (!names.has(link)) ghosts.add(link);
}
}
return [...ghosts];
}
static normalize(m?: Memory[] | MemoryCache | MemoryOptions) {
if(!m) return null;
const raw = m instanceof MemoryCache || Array.isArray(m);
@@ -458,6 +469,8 @@ ${currentBody}
private async factAgent(conversation: string, memories: Memory[], options: LLMRequest, weekKey: string): Promise<FactBucket[]> {
const buckets = new Map<string, string[]>();
const ghosts = this.ghostNodes(memories);
await this.llm.ask(conversation, {
model: options.model,
temperature: 0.2,
@@ -472,14 +485,15 @@ Rules:
- If nothing worth remembering was said, do not call any tools
When extracting facts, you MUST also decide the exact destination path:
- Use an existing node name if the facts clearly belong there
- Reuse node names (including ghost) as much as possible IF the facts belongs there
- All information primarily about the user should go under "People/User"
- When required, create a new path following collection/subject format (e.g., People/Sarah, Projects/Oxide) — you are not limited to any fixed list of collections, use whatever fits
- For journal entries, use "Journal"
Available nodes:
- Journal
${this.listNodes(memories).filter(n => !n.name.includes('Journal')).map(n => `- ${n.name}: ${n.description}`).join('\n') || 'None yet.'}`,
${this.listNodes(memories).filter(n => !n.name.includes('Journal')).map(n => `- ${n.name}: ${n.description}`).join('\n') || 'None yet.'}
${ghosts.length ? `${ghosts.map(g => `- ${g}: (Ghost)`).join('\n')}` : ''}`,
tools: [{
name: 'facts_extract',
description: 'Submit facts with their destination',