@compilr-dev/sdk 0.10.26 → 0.10.28

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -45,7 +45,7 @@ export const webTools = [webFetchTool];
45
45
  */
46
46
  const todoStore = getDefaultTodoStore();
47
47
  const todoTools = createTodoTools(todoStore);
48
- const lenientTodoWrite = createLenientTodoWriteTool(todoTools.todoWrite);
48
+ const lenientTodoWrite = createLenientTodoWriteTool(todoTools.todoWrite, todoStore);
49
49
  const lenientTodoClaim = createLenientTodoClaimTool(todoTools.todoClaim, todoStore);
50
50
  const lenientTodoHandoff = createLenientTodoHandoffTool(todoTools.todoHandoff, todoStore);
51
51
  /**
@@ -24,7 +24,7 @@ Guidelines:
24
24
  // drop when the LLM picks the wrong key.
25
25
  const todoStore = getDefaultTodoStore();
26
26
  const todoTools = createTodoTools(todoStore);
27
- const lenientTodoWrite = createLenientTodoWriteTool(todoTools.todoWrite);
27
+ const lenientTodoWrite = createLenientTodoWriteTool(todoTools.todoWrite, todoStore);
28
28
  const lenientTodoClaim = createLenientTodoClaimTool(todoTools.todoClaim, todoStore);
29
29
  const lenientTodoHandoff = createLenientTodoHandoffTool(todoTools.todoHandoff, todoStore);
30
30
  export const generalPreset = {
@@ -44,14 +44,25 @@ interface StrictTodoInput {
44
44
  }>;
45
45
  }
46
46
  /**
47
- * Wrap a strict `todo_write` tool with input-key normalisation.
47
+ * Wrap a strict `todo_write` tool with input-key normalisation AND
48
+ * response enrichment.
48
49
  *
49
- * Pass the `todoWrite` from `createTodoTools(store)` (or the standalone
50
- * `todoWriteTool` export). Returns a Tool with the same name (`todo_write`)
51
- * and the same schema, but with an `execute` that normalises input
52
- * before delegating.
50
+ * Two improvements over the strict tool:
51
+ *
52
+ * 1. **Input normalisation**: accepts alternative property aliases LLMs
53
+ * commonly emit (`title`, `description`, `task`, `text` → `content`).
54
+ * Prevents silent todo drops when the LLM picks the wrong key.
55
+ *
56
+ * 2. **Response enrichment**: after writing, returns the full todo list
57
+ * *with ids* so subsequent `todo_claim` / `todo_handoff` calls have
58
+ * the right `todoId` without needing an extra `todo_read` round trip.
59
+ * Per agent feedback: "if todo_write returned the ids, that would
60
+ * streamline the process."
61
+ *
62
+ * Pass the `todoWrite` from `createTodoTools(store)` and the same store
63
+ * instance — the wrapper reads the store after writing to surface ids.
53
64
  */
54
- export declare function createLenientTodoWriteTool(strictTool: Tool<StrictTodoInput>): Tool<LenientTodoInput>;
65
+ export declare function createLenientTodoWriteTool(strictTool: Tool<StrictTodoInput>, store: TodoStore): Tool<LenientTodoInput>;
55
66
  interface LenientTodoClaimInput {
56
67
  todoId: string | number;
57
68
  agentId: string;
@@ -20,17 +20,29 @@
20
20
  */
21
21
  import { defineTool, } from '@compilr-dev/agents';
22
22
  /**
23
- * Wrap a strict `todo_write` tool with input-key normalisation.
23
+ * Wrap a strict `todo_write` tool with input-key normalisation AND
24
+ * response enrichment.
24
25
  *
25
- * Pass the `todoWrite` from `createTodoTools(store)` (or the standalone
26
- * `todoWriteTool` export). Returns a Tool with the same name (`todo_write`)
27
- * and the same schema, but with an `execute` that normalises input
28
- * before delegating.
26
+ * Two improvements over the strict tool:
27
+ *
28
+ * 1. **Input normalisation**: accepts alternative property aliases LLMs
29
+ * commonly emit (`title`, `description`, `task`, `text` → `content`).
30
+ * Prevents silent todo drops when the LLM picks the wrong key.
31
+ *
32
+ * 2. **Response enrichment**: after writing, returns the full todo list
33
+ * *with ids* so subsequent `todo_claim` / `todo_handoff` calls have
34
+ * the right `todoId` without needing an extra `todo_read` round trip.
35
+ * Per agent feedback: "if todo_write returned the ids, that would
36
+ * streamline the process."
37
+ *
38
+ * Pass the `todoWrite` from `createTodoTools(store)` and the same store
39
+ * instance — the wrapper reads the store after writing to surface ids.
29
40
  */
30
- export function createLenientTodoWriteTool(strictTool) {
41
+ export function createLenientTodoWriteTool(strictTool, store) {
31
42
  return defineTool({
32
43
  name: 'todo_write',
33
- description: strictTool.definition.description,
44
+ description: `${strictTool.definition.description} ` +
45
+ 'Returns the resulting todo list with ids — use those ids for any subsequent todo_claim / todo_handoff calls.',
34
46
  inputSchema: strictTool.definition.inputSchema,
35
47
  execute: async (input) => {
36
48
  // Normalise — accept alternative property names that LLMs commonly emit.
@@ -53,38 +65,59 @@ export function createLenientTodoWriteTool(strictTool) {
53
65
  ...todo,
54
66
  content: todo.content || 'Untitled task',
55
67
  }));
56
- return strictTool.execute({ todos: valid });
68
+ const result = await strictTool.execute({ todos: valid });
69
+ // Enrich the success result with the actual stored todos (incl. ids)
70
+ // so the LLM has them in context immediately — no todo_read needed.
71
+ if (result.success) {
72
+ const todos = store.getAll().map((t) => ({
73
+ id: t.id,
74
+ content: t.content,
75
+ status: t.status,
76
+ owner: t.owner,
77
+ activeForm: t.activeForm,
78
+ priority: t.priority,
79
+ }));
80
+ const inner = result.result && typeof result.result === 'object'
81
+ ? result.result
82
+ : {};
83
+ return {
84
+ success: true,
85
+ result: { ...inner, todos },
86
+ };
87
+ }
88
+ return result;
57
89
  },
58
90
  });
59
91
  }
60
92
  /**
61
93
  * Resolve a lenient todoId reference to a concrete store key.
62
94
  * Returns null if no plausible match exists.
95
+ *
96
+ * Strategy intentionally narrow — only unambiguous one-to-one mappings.
97
+ * No content/prefix/substring matching: fuzzy resolution risks silent
98
+ * wrong matches in edge cases (e.g. "Fix login" matching the wrong of
99
+ * two similar todos). The agent should either pass the full id from a
100
+ * prior `todo_write` / `todo_read` response, or use the numeric position
101
+ * shown in the footer. Anything else gets a clear error listing the
102
+ * available ids, which the agent can recover from in one extra call.
63
103
  */
64
104
  function resolveTodoId(rawId, store) {
65
- const all = store.getAll();
66
- const ids = all.map((t) => t.id);
67
- // String coercion + trim.
105
+ const ids = store.getAll().map((t) => t.id);
68
106
  const id = String(rawId).trim();
69
107
  // Direct match — handles "todo-1" as-is.
70
108
  if (ids.includes(id))
71
109
  return id;
72
- // Bare positional reference: "1" or 1 → "todo-1".
110
+ // Bare positional reference: "1" or 1 → "todo-1". Matches the footer's
111
+ // "#1 ☐ task" rendering.
73
112
  if (/^\d+$/.test(id)) {
74
113
  const candidate = `todo-${id}`;
75
114
  if (ids.includes(candidate))
76
115
  return candidate;
77
116
  }
78
- // Allow "todo_1" variant.
117
+ // "todo_1" variant — LLMs sometimes use underscores.
79
118
  const underscoreToHyphen = id.replace(/^todo_/, 'todo-');
80
119
  if (ids.includes(underscoreToHyphen))
81
120
  return underscoreToHyphen;
82
- // Last resort: content-prefix match (case-insensitive). Only commits to
83
- // a hit if exactly one todo's content starts with the given string.
84
- const lower = id.toLowerCase();
85
- const contentHits = all.filter((t) => t.content.toLowerCase().startsWith(lower));
86
- if (contentHits.length === 1)
87
- return contentHits[0].id;
88
121
  return null;
89
122
  }
90
123
  /**
@@ -188,6 +188,24 @@ export class TeamAgent {
188
188
  }
189
189
  // Build system prompt addition with shared context
190
190
  let finalSystemPromptAddition = this.systemPromptAddition ?? '';
191
+ // Identity preamble — every team agent gets a stable self-reference
192
+ // block at the top of its prompt addition. Without this, the agent
193
+ // knows it's "the PM" from the role prompt but doesn't connect that
194
+ // to `agentId: "pm"` when tools take an agentId parameter. User test
195
+ // 2026-06-04 caught this: $pm called `todo_read(owner="default")`
196
+ // because it didn't know its id was "pm".
197
+ //
198
+ // Uses runtime fields (this.id, this.displayName, this.mascot) so
199
+ // collision-renamed ids (e.g. "pm-2") are correctly stated.
200
+ const identityBlock = `# YOUR IDENTITY IN THIS TEAM\n\n` +
201
+ `You are agent **${this.displayName}** ${this.mascot}.\n\n` +
202
+ `- Your agent ID: \`${this.id}\`\n` +
203
+ `- When tools have an \`agentId\` (or \`owner\`, \`toAgentId\`) parameter referring to YOU, use \`"${this.id}"\`.\n` +
204
+ `- When users mention \`$${this.id}\`, they mean you.\n` +
205
+ `- When asked "who are you?" or "what is your role?", state both your role and your agent ID.`;
206
+ finalSystemPromptAddition = finalSystemPromptAddition
207
+ ? `${identityBlock}\n\n${finalSystemPromptAddition}`
208
+ : identityBlock;
191
209
  // Inject shared context if provided (excluding roster — that goes via anchor for live updates)
192
210
  if (sharedContext) {
193
211
  const sharedContextBlock = sharedContext.format({ excludeRoster: true });
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@compilr-dev/sdk",
3
- "version": "0.10.26",
3
+ "version": "0.10.28",
4
4
  "description": "Universal agent runtime for building AI-powered applications",
5
5
  "type": "module",
6
6
  "main": "dist/index.js",