@siftd/connect-agent 0.2.46 → 0.2.48
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.
- package/dist/orchestrator.d.ts +8 -1
- package/dist/orchestrator.js +329 -141
- package/dist/tools/worker.d.ts +1 -0
- package/dist/tools/worker.js +3 -1
- package/dist/websocket.d.ts +4 -1
- package/dist/websocket.js +25 -0
- package/package.json +1 -1
package/dist/orchestrator.d.ts
CHANGED
|
@@ -66,6 +66,7 @@ export declare class MasterOrchestrator {
|
|
|
66
66
|
private workerLogCallback;
|
|
67
67
|
private workerStatusInterval;
|
|
68
68
|
private attachmentContext;
|
|
69
|
+
private lastUserMessage;
|
|
69
70
|
private galleryCallback;
|
|
70
71
|
private userId;
|
|
71
72
|
private workspaceDir;
|
|
@@ -193,7 +194,13 @@ export declare class MasterOrchestrator {
|
|
|
193
194
|
private stripTodoSnapshot;
|
|
194
195
|
private hasTodoMutation;
|
|
195
196
|
private hasCalendarMutation;
|
|
196
|
-
private
|
|
197
|
+
private hasFileMutation;
|
|
198
|
+
private getUserTagHint;
|
|
199
|
+
private getBreakdownTarget;
|
|
200
|
+
private ensureBreakdownOriginalDone;
|
|
201
|
+
private getLocalDateKey;
|
|
202
|
+
private getLocalTimeZone;
|
|
203
|
+
private getTodoCalSystemPrompt;
|
|
197
204
|
private getToolChoice;
|
|
198
205
|
private withAttachments;
|
|
199
206
|
private updateFileScope;
|
package/dist/orchestrator.js
CHANGED
|
@@ -157,7 +157,9 @@ CALENDAR + TODO (Lia-managed data):
|
|
|
157
157
|
- Keep these files hidden; refer users to /cal or /todo in responses
|
|
158
158
|
- If the user provides subtasks or a bullet list under one task, store them as subtasks (unless they explicitly ask to split into multiple items)
|
|
159
159
|
- Preserve explicit due dates, times, and priorities exactly as given (YYYY-MM-DD, HH:MM)
|
|
160
|
+
- Preserve task titles verbatim (including bracketed tags like [lia-test-...], quoted titles, and casing). Do not strip tags or rewrite titles.
|
|
160
161
|
- If the user explicitly requests /todo or /cal changes, you MUST call the corresponding tool in the same response
|
|
162
|
+
- Never delegate /todo or /cal changes to workers; use the tools directly in the same response
|
|
161
163
|
- If the user says "Add to /todo:" followed by a list of deadlines/details, create ONE todo with notes unless they explicitly ask to split/break down
|
|
162
164
|
- Schemas:
|
|
163
165
|
- calendar.json: { "version": 1, "calendars": [...], "events": [...] }
|
|
@@ -185,6 +187,16 @@ Before complex work: Check CLAUDE.md → Read LANDMARKS.md → Search memory
|
|
|
185
187
|
After completing work: Update LANDMARKS.md → Remember learnings
|
|
186
188
|
|
|
187
189
|
You orchestrate through workers. You remember through memory. You never do arbitrary file operations directly (calendar_upsert_events and todo_upsert_items are the safe exceptions).`;
|
|
190
|
+
const TODO_CAL_SYSTEM_PROMPT_BASE = `You are Lia. Your ONLY job is to update /todo and /cal using tools.
|
|
191
|
+
|
|
192
|
+
Rules:
|
|
193
|
+
- If the user requests /todo changes, call todo_upsert_items.
|
|
194
|
+
- If the user requests /cal or calendar changes, call calendar_upsert_events.
|
|
195
|
+
- If both are requested, call BOTH tools in the same response.
|
|
196
|
+
- Preserve exact titles, dates, times, notes, and any bracketed tags (e.g., [lia-test-...]).
|
|
197
|
+
- Do not spawn workers or call any other tools.
|
|
198
|
+
- Do not ask follow-up questions; infer reasonable defaults from the request.
|
|
199
|
+
- After tool calls, respond briefly confirming the updates.`;
|
|
188
200
|
export class MasterOrchestrator {
|
|
189
201
|
client;
|
|
190
202
|
model;
|
|
@@ -206,6 +218,7 @@ export class MasterOrchestrator {
|
|
|
206
218
|
workerLogCallback = null;
|
|
207
219
|
workerStatusInterval = null;
|
|
208
220
|
attachmentContext = null;
|
|
221
|
+
lastUserMessage = null;
|
|
209
222
|
galleryCallback = null;
|
|
210
223
|
userId;
|
|
211
224
|
workspaceDir;
|
|
@@ -688,8 +701,73 @@ export class MasterOrchestrator {
|
|
|
688
701
|
const query = /\b(what|show|list|open|view|see)\b/.test(lower);
|
|
689
702
|
return target && action && !query;
|
|
690
703
|
}
|
|
691
|
-
|
|
692
|
-
|
|
704
|
+
hasFileMutation(message) {
|
|
705
|
+
const lower = this.stripTodoSnapshot(message).toLowerCase();
|
|
706
|
+
const target = /(^|\s)\/files\b|\bfiles?\b/.test(lower);
|
|
707
|
+
const action = /\b(create|make|write|save|generate|export|upload|attach|produce|edit|update|modify|delete|remove)\b/.test(lower);
|
|
708
|
+
const query = /\b(what|show|list|open|view|see|browse|find)\b/.test(lower);
|
|
709
|
+
return target && action && !query;
|
|
710
|
+
}
|
|
711
|
+
getUserTagHint(message) {
|
|
712
|
+
if (!message)
|
|
713
|
+
return null;
|
|
714
|
+
const base = this.stripTodoSnapshot(message);
|
|
715
|
+
const matches = base.match(/\[[^\]]{2,64}\]/g) || [];
|
|
716
|
+
const unique = Array.from(new Set(matches.map((tag) => tag.trim()))).filter(Boolean);
|
|
717
|
+
if (unique.length !== 1)
|
|
718
|
+
return null;
|
|
719
|
+
return unique[0];
|
|
720
|
+
}
|
|
721
|
+
getBreakdownTarget(message) {
|
|
722
|
+
if (!message)
|
|
723
|
+
return null;
|
|
724
|
+
const stripped = this.stripTodoSnapshot(message);
|
|
725
|
+
if (!/\b(break(?:\s|-)?down|split|parse)\b/i.test(stripped))
|
|
726
|
+
return null;
|
|
727
|
+
const quoted = stripped.match(/["“]([^"”]{3,160})["”]/);
|
|
728
|
+
if (quoted && quoted[1]) {
|
|
729
|
+
return quoted[1].trim();
|
|
730
|
+
}
|
|
731
|
+
const inline = stripped.match(/break(?:\s|-)?down\s+the\s+(.+?)\s+(?:item|todo|task)\b/i);
|
|
732
|
+
if (inline && inline[1]) {
|
|
733
|
+
return inline[1].trim();
|
|
734
|
+
}
|
|
735
|
+
return null;
|
|
736
|
+
}
|
|
737
|
+
ensureBreakdownOriginalDone(items, targetTitle) {
|
|
738
|
+
const normalizedTarget = targetTitle.trim().toLowerCase();
|
|
739
|
+
let found = false;
|
|
740
|
+
const nextItems = items.map((item) => {
|
|
741
|
+
if (item?.title && item.title.trim().toLowerCase() === normalizedTarget) {
|
|
742
|
+
found = true;
|
|
743
|
+
return { ...item, done: true };
|
|
744
|
+
}
|
|
745
|
+
return item;
|
|
746
|
+
});
|
|
747
|
+
if (!found) {
|
|
748
|
+
nextItems.unshift({ title: targetTitle, done: true });
|
|
749
|
+
}
|
|
750
|
+
return nextItems;
|
|
751
|
+
}
|
|
752
|
+
getLocalDateKey(date = new Date()) {
|
|
753
|
+
const year = date.getFullYear();
|
|
754
|
+
const month = `${date.getMonth() + 1}`.padStart(2, '0');
|
|
755
|
+
const day = `${date.getDate()}`.padStart(2, '0');
|
|
756
|
+
return `${year}-${month}-${day}`;
|
|
757
|
+
}
|
|
758
|
+
getLocalTimeZone() {
|
|
759
|
+
try {
|
|
760
|
+
return Intl.DateTimeFormat().resolvedOptions().timeZone || null;
|
|
761
|
+
}
|
|
762
|
+
catch {
|
|
763
|
+
return null;
|
|
764
|
+
}
|
|
765
|
+
}
|
|
766
|
+
getTodoCalSystemPrompt() {
|
|
767
|
+
const today = this.getLocalDateKey();
|
|
768
|
+
const timeZone = this.getLocalTimeZone();
|
|
769
|
+
const tzNote = timeZone ? ` (${timeZone})` : '';
|
|
770
|
+
return `${TODO_CAL_SYSTEM_PROMPT_BASE}\n\nDate context:\n- Today is ${today}${tzNote}.\n- Use this to resolve relative dates like "today", "tomorrow", or "next week".`;
|
|
693
771
|
}
|
|
694
772
|
getToolChoice(messages) {
|
|
695
773
|
const last = messages[messages.length - 1];
|
|
@@ -697,12 +775,16 @@ export class MasterOrchestrator {
|
|
|
697
775
|
return undefined;
|
|
698
776
|
const wantsTodo = this.hasTodoMutation(last.content);
|
|
699
777
|
const wantsCal = this.hasCalendarMutation(last.content);
|
|
778
|
+
const wantsFiles = this.hasFileMutation(last.content);
|
|
700
779
|
if (wantsTodo && !wantsCal) {
|
|
701
780
|
return { type: 'tool', name: 'todo_upsert_items' };
|
|
702
781
|
}
|
|
703
|
-
if (wantsCal && !wantsTodo
|
|
782
|
+
if (wantsCal && !wantsTodo) {
|
|
704
783
|
return { type: 'tool', name: 'calendar_upsert_events' };
|
|
705
784
|
}
|
|
785
|
+
if (wantsFiles) {
|
|
786
|
+
return { type: 'tool', name: 'delegate_to_worker' };
|
|
787
|
+
}
|
|
706
788
|
return undefined;
|
|
707
789
|
}
|
|
708
790
|
withAttachments(task, context) {
|
|
@@ -775,12 +857,28 @@ export class MasterOrchestrator {
|
|
|
775
857
|
* Process a user message
|
|
776
858
|
*/
|
|
777
859
|
async processMessage(message, conversationHistory = [], sendMessage) {
|
|
860
|
+
this.lastUserMessage = message;
|
|
778
861
|
// Handle slash commands first
|
|
779
862
|
const slashResponse = this.handleSlashCommand(message);
|
|
780
863
|
if (slashResponse) {
|
|
781
864
|
return slashResponse;
|
|
782
865
|
}
|
|
783
866
|
this.updateFileScope(message);
|
|
867
|
+
const wantsTodoOrCal = this.hasTodoMutation(message) || this.hasCalendarMutation(message);
|
|
868
|
+
if (wantsTodoOrCal) {
|
|
869
|
+
this.attachmentContext = this.extractAttachmentContext(message);
|
|
870
|
+
const toolMessages = [{ role: 'user', content: message }];
|
|
871
|
+
try {
|
|
872
|
+
return await this.runAgentLoop(toolMessages, this.getTodoCalSystemPrompt(), sendMessage);
|
|
873
|
+
}
|
|
874
|
+
catch (error) {
|
|
875
|
+
const errorMessage = error instanceof Error ? error.message : String(error);
|
|
876
|
+
return `Error: ${errorMessage}`;
|
|
877
|
+
}
|
|
878
|
+
finally {
|
|
879
|
+
this.attachmentContext = null;
|
|
880
|
+
}
|
|
881
|
+
}
|
|
784
882
|
// DISABLED: Dumb regex extraction was creating garbage todos
|
|
785
883
|
// Let the AI use calendar_upsert_events and todo_upsert_items tools properly
|
|
786
884
|
// const quickWrite = this.tryHandleCalendarTodo(message);
|
|
@@ -1231,14 +1329,34 @@ ${hubContextStr}
|
|
|
1231
1329
|
* Run the agentic loop
|
|
1232
1330
|
*/
|
|
1233
1331
|
async runAgentLoop(messages, system, sendMessage) {
|
|
1234
|
-
const
|
|
1332
|
+
const toolsBase = this.getToolDefinitions();
|
|
1333
|
+
const last = messages[messages.length - 1];
|
|
1334
|
+
const lastContent = last && last.role === 'user' && typeof last.content === 'string' ? last.content : '';
|
|
1335
|
+
const restrictWorkers = lastContent
|
|
1336
|
+
? (this.hasTodoMutation(lastContent) || this.hasCalendarMutation(lastContent))
|
|
1337
|
+
: false;
|
|
1338
|
+
const wantsTodoOrCal = restrictWorkers;
|
|
1339
|
+
const todoCalTools = new Set(['calendar_upsert_events', 'todo_upsert_items']);
|
|
1340
|
+
const blockedTools = new Set([
|
|
1341
|
+
'spawn_worker',
|
|
1342
|
+
'delegate_to_worker',
|
|
1343
|
+
'check_worker',
|
|
1344
|
+
'wait_worker',
|
|
1345
|
+
'list_workers',
|
|
1346
|
+
'cancel_worker',
|
|
1347
|
+
]);
|
|
1348
|
+
const tools = wantsTodoOrCal
|
|
1349
|
+
? toolsBase.filter((tool) => todoCalTools.has(tool.name))
|
|
1350
|
+
: toolsBase.filter((tool) => !blockedTools.has(tool.name) || !restrictWorkers);
|
|
1235
1351
|
let currentMessages = [...messages];
|
|
1236
1352
|
let iterations = 0;
|
|
1237
1353
|
const maxIterations = 30; // Increased for complex multi-tool tasks
|
|
1238
1354
|
const requestTimeoutMs = 60000;
|
|
1355
|
+
const forcedToolChoice = this.getToolChoice(currentMessages);
|
|
1356
|
+
let retriedForcedTool = false;
|
|
1239
1357
|
while (iterations < maxIterations) {
|
|
1240
1358
|
iterations++;
|
|
1241
|
-
const toolChoice = this.getToolChoice(currentMessages);
|
|
1359
|
+
const toolChoice = forcedToolChoice ?? this.getToolChoice(currentMessages);
|
|
1242
1360
|
const requestStart = Date.now();
|
|
1243
1361
|
console.log(`[ORCHESTRATOR] Anthropic request ${iterations}/${maxIterations} (model: ${this.model})`);
|
|
1244
1362
|
const response = await Promise.race([
|
|
@@ -1259,10 +1377,37 @@ ${hubContextStr}
|
|
|
1259
1377
|
console.log(`[ORCHESTRATOR] Anthropic response ${iterations}/${maxIterations} in ${Date.now() - requestStart}ms`);
|
|
1260
1378
|
// Check if done
|
|
1261
1379
|
if (response.stop_reason === 'end_turn' || !this.hasToolUse(response.content)) {
|
|
1380
|
+
if (forcedToolChoice && !retriedForcedTool) {
|
|
1381
|
+
retriedForcedTool = true;
|
|
1382
|
+
const toolName = forcedToolChoice.name;
|
|
1383
|
+
const needsNoWorkers = toolName === 'todo_upsert_items' || toolName === 'calendar_upsert_events';
|
|
1384
|
+
const followup = needsNoWorkers
|
|
1385
|
+
? `You must call the ${toolName} tool now. Use the exact task/event titles and any bracketed tags exactly as provided. Do not spawn workers.`
|
|
1386
|
+
: `You must call the ${toolName} tool now. Use the user's request as the task details.`;
|
|
1387
|
+
currentMessages = [
|
|
1388
|
+
...currentMessages,
|
|
1389
|
+
{ role: 'assistant', content: response.content },
|
|
1390
|
+
{
|
|
1391
|
+
role: 'user',
|
|
1392
|
+
content: followup
|
|
1393
|
+
}
|
|
1394
|
+
];
|
|
1395
|
+
continue;
|
|
1396
|
+
}
|
|
1262
1397
|
return this.extractText(response.content);
|
|
1263
1398
|
}
|
|
1399
|
+
const toolUseBlocks = response.content.filter((block) => block.type === 'tool_use');
|
|
1400
|
+
const toolNames = toolUseBlocks.map((block) => block.name);
|
|
1264
1401
|
// Process tool calls
|
|
1265
1402
|
const toolResults = await this.processToolCalls(response.content, sendMessage);
|
|
1403
|
+
if (wantsTodoOrCal && (toolNames.includes('todo_upsert_items') || toolNames.includes('calendar_upsert_events'))) {
|
|
1404
|
+
const updates = [];
|
|
1405
|
+
if (toolNames.includes('todo_upsert_items'))
|
|
1406
|
+
updates.push('Updated /todo.');
|
|
1407
|
+
if (toolNames.includes('calendar_upsert_events'))
|
|
1408
|
+
updates.push('Updated /cal.');
|
|
1409
|
+
return `${updates.join(' ')} Open /todo or /cal to view.`;
|
|
1410
|
+
}
|
|
1266
1411
|
// Continue conversation
|
|
1267
1412
|
currentMessages = [
|
|
1268
1413
|
...currentMessages,
|
|
@@ -1878,6 +2023,17 @@ Unlike lia_plan (internal only), this creates a VISIBLE todo list that appears i
|
|
|
1878
2023
|
async processToolCalls(content, sendMessage) {
|
|
1879
2024
|
const toolUseBlocks = content.filter((block) => block.type === 'tool_use');
|
|
1880
2025
|
const results = [];
|
|
2026
|
+
const wantsTodoOrCal = this.lastUserMessage
|
|
2027
|
+
? (this.hasTodoMutation(this.lastUserMessage) || this.hasCalendarMutation(this.lastUserMessage))
|
|
2028
|
+
: false;
|
|
2029
|
+
const blockedWorkerTools = new Set([
|
|
2030
|
+
'spawn_worker',
|
|
2031
|
+
'delegate_to_worker',
|
|
2032
|
+
'check_worker',
|
|
2033
|
+
'wait_worker',
|
|
2034
|
+
'list_workers',
|
|
2035
|
+
'cancel_worker',
|
|
2036
|
+
]);
|
|
1881
2037
|
for (const toolUse of toolUseBlocks) {
|
|
1882
2038
|
const input = toolUse.input;
|
|
1883
2039
|
let result;
|
|
@@ -1887,151 +2043,183 @@ Unlike lia_plan (internal only), this creates a VISIBLE todo list that appears i
|
|
|
1887
2043
|
if (preview)
|
|
1888
2044
|
await sendMessage(preview);
|
|
1889
2045
|
}
|
|
1890
|
-
|
|
1891
|
-
|
|
1892
|
-
|
|
1893
|
-
|
|
1894
|
-
|
|
1895
|
-
|
|
1896
|
-
|
|
1897
|
-
|
|
1898
|
-
|
|
1899
|
-
|
|
1900
|
-
|
|
1901
|
-
|
|
1902
|
-
|
|
1903
|
-
|
|
1904
|
-
|
|
1905
|
-
|
|
1906
|
-
|
|
1907
|
-
|
|
1908
|
-
|
|
1909
|
-
|
|
1910
|
-
|
|
1911
|
-
|
|
1912
|
-
|
|
1913
|
-
|
|
1914
|
-
|
|
1915
|
-
|
|
1916
|
-
|
|
1917
|
-
|
|
1918
|
-
|
|
1919
|
-
|
|
1920
|
-
|
|
1921
|
-
|
|
1922
|
-
|
|
1923
|
-
|
|
1924
|
-
|
|
1925
|
-
|
|
1926
|
-
|
|
1927
|
-
|
|
1928
|
-
|
|
1929
|
-
|
|
1930
|
-
|
|
1931
|
-
|
|
1932
|
-
|
|
1933
|
-
|
|
1934
|
-
|
|
1935
|
-
|
|
1936
|
-
|
|
1937
|
-
|
|
1938
|
-
|
|
1939
|
-
|
|
1940
|
-
|
|
1941
|
-
|
|
1942
|
-
|
|
1943
|
-
|
|
1944
|
-
|
|
1945
|
-
|
|
1946
|
-
|
|
1947
|
-
|
|
1948
|
-
|
|
2046
|
+
if (wantsTodoOrCal && blockedWorkerTools.has(toolUse.name)) {
|
|
2047
|
+
result = {
|
|
2048
|
+
success: false,
|
|
2049
|
+
output: '',
|
|
2050
|
+
error: 'Do not use worker tools for /todo or /cal. Call todo_upsert_items or calendar_upsert_events directly.'
|
|
2051
|
+
};
|
|
2052
|
+
}
|
|
2053
|
+
else
|
|
2054
|
+
switch (toolUse.name) {
|
|
2055
|
+
// New direct tools
|
|
2056
|
+
case 'bash':
|
|
2057
|
+
result = await this.bashTool.execute(input.command, input.timeout);
|
|
2058
|
+
break;
|
|
2059
|
+
case 'calendar_upsert_events':
|
|
2060
|
+
{
|
|
2061
|
+
const tagHint = this.getUserTagHint(this.lastUserMessage);
|
|
2062
|
+
const events = input.events || [];
|
|
2063
|
+
const taggedEvents = tagHint
|
|
2064
|
+
? events.map((event) => ({
|
|
2065
|
+
...event,
|
|
2066
|
+
title: event.title.includes(tagHint) ? event.title : `${event.title} ${tagHint}`
|
|
2067
|
+
}))
|
|
2068
|
+
: events;
|
|
2069
|
+
result = this.calendarTools.upsertCalendarEvents(taggedEvents, input.calendars);
|
|
2070
|
+
}
|
|
2071
|
+
break;
|
|
2072
|
+
case 'todo_upsert_items':
|
|
2073
|
+
{
|
|
2074
|
+
const tagHint = this.getUserTagHint(this.lastUserMessage);
|
|
2075
|
+
let items = input.items || [];
|
|
2076
|
+
const breakdownTarget = this.getBreakdownTarget(this.lastUserMessage);
|
|
2077
|
+
if (breakdownTarget) {
|
|
2078
|
+
items = this.ensureBreakdownOriginalDone(items, breakdownTarget);
|
|
2079
|
+
}
|
|
2080
|
+
const taggedItems = tagHint
|
|
2081
|
+
? items.map((item) => ({
|
|
2082
|
+
...item,
|
|
2083
|
+
title: item.title.includes(tagHint) ? item.title : `${item.title} ${tagHint}`
|
|
2084
|
+
}))
|
|
2085
|
+
: items;
|
|
2086
|
+
result = this.calendarTools.upsertTodoItems(taggedItems);
|
|
2087
|
+
}
|
|
2088
|
+
break;
|
|
2089
|
+
case 'web_search':
|
|
2090
|
+
result = await this.webTools.webSearch(input.query, { numResults: input.num_results });
|
|
2091
|
+
break;
|
|
2092
|
+
case 'fetch_url':
|
|
2093
|
+
result = await this.webTools.fetchUrl(input.url, {
|
|
2094
|
+
format: input.format,
|
|
2095
|
+
timeout: input.timeout
|
|
2096
|
+
});
|
|
2097
|
+
break;
|
|
2098
|
+
// Worker tools
|
|
2099
|
+
case 'spawn_worker': {
|
|
2100
|
+
const { task } = this.withAttachments(input.task);
|
|
2101
|
+
const fileScope = this.getFileScopeOverrides();
|
|
2102
|
+
const scopedTask = fileScope.instructions ? `${fileScope.instructions}\n\n${task}` : task;
|
|
2103
|
+
result = await this.workerTools.spawnWorker(scopedTask, {
|
|
2104
|
+
timeout: input.timeout,
|
|
2105
|
+
priority: input.priority,
|
|
2106
|
+
workingDirectory: input.working_directory || fileScope.workingDir
|
|
2107
|
+
});
|
|
1949
2108
|
break;
|
|
1950
2109
|
}
|
|
1951
|
-
|
|
1952
|
-
|
|
1953
|
-
|
|
1954
|
-
|
|
1955
|
-
|
|
1956
|
-
|
|
1957
|
-
|
|
1958
|
-
|
|
1959
|
-
|
|
1960
|
-
|
|
1961
|
-
|
|
1962
|
-
|
|
1963
|
-
|
|
1964
|
-
|
|
1965
|
-
|
|
2110
|
+
case 'check_worker':
|
|
2111
|
+
result = await this.workerTools.checkWorker(input.job_id);
|
|
2112
|
+
break;
|
|
2113
|
+
case 'wait_worker':
|
|
2114
|
+
result = await this.workerTools.waitWorker(input.job_id, input.max_wait);
|
|
2115
|
+
break;
|
|
2116
|
+
case 'list_workers':
|
|
2117
|
+
result = await this.workerTools.listWorkers(input.status);
|
|
2118
|
+
break;
|
|
2119
|
+
case 'cancel_worker':
|
|
2120
|
+
result = await this.workerTools.cancelWorker(input.job_id);
|
|
2121
|
+
break;
|
|
2122
|
+
// Legacy delegate tool
|
|
2123
|
+
case 'delegate_to_worker': {
|
|
2124
|
+
const { task, context } = this.withAttachments(input.task, input.context);
|
|
2125
|
+
const fileScope = this.getFileScopeOverrides();
|
|
2126
|
+
result = await this.delegateToWorker(task, context, input.working_directory || fileScope.workingDir, fileScope.instructions);
|
|
1966
2127
|
break;
|
|
1967
2128
|
}
|
|
1968
|
-
|
|
1969
|
-
|
|
1970
|
-
|
|
1971
|
-
|
|
1972
|
-
|
|
1973
|
-
|
|
1974
|
-
|
|
1975
|
-
|
|
1976
|
-
|
|
1977
|
-
|
|
1978
|
-
|
|
1979
|
-
|
|
1980
|
-
|
|
1981
|
-
|
|
1982
|
-
|
|
1983
|
-
|
|
2129
|
+
case 'remember':
|
|
2130
|
+
result = await this.executeRemember(input.content, input.type, input.importance);
|
|
2131
|
+
break;
|
|
2132
|
+
case 'remember_entity': {
|
|
2133
|
+
const scope = input.scope === 'org' ? 'org' : 'user';
|
|
2134
|
+
const graph = scope === 'org' ? this.orgContextGraph : this.contextGraph;
|
|
2135
|
+
if (!graph) {
|
|
2136
|
+
result = { success: false, output: 'Org context is not configured for this agent.' };
|
|
2137
|
+
break;
|
|
2138
|
+
}
|
|
2139
|
+
const entity = await graph.upsertEntity({
|
|
2140
|
+
kind: input.kind,
|
|
2141
|
+
name: input.name,
|
|
2142
|
+
description: input.description,
|
|
2143
|
+
attributes: input.attributes,
|
|
2144
|
+
tags: input.tags
|
|
2145
|
+
});
|
|
2146
|
+
result = { success: true, output: `Stored ${scope} entity: ${entity.kind} ${entity.name} (${entity.key})` };
|
|
1984
2147
|
break;
|
|
1985
2148
|
}
|
|
1986
|
-
|
|
1987
|
-
|
|
1988
|
-
|
|
2149
|
+
case 'remember_relation': {
|
|
2150
|
+
const scope = input.scope === 'org' ? 'org' : 'user';
|
|
2151
|
+
const graph = scope === 'org' ? this.orgContextGraph : this.contextGraph;
|
|
2152
|
+
if (!graph) {
|
|
2153
|
+
result = { success: false, output: 'Org context is not configured for this agent.' };
|
|
2154
|
+
break;
|
|
2155
|
+
}
|
|
2156
|
+
const relation = await graph.linkEntities({
|
|
2157
|
+
from: input.from,
|
|
2158
|
+
to: input.to,
|
|
2159
|
+
type: input.type,
|
|
2160
|
+
description: input.description,
|
|
2161
|
+
fromKind: input.from_kind,
|
|
2162
|
+
toKind: input.to_kind
|
|
2163
|
+
});
|
|
2164
|
+
result = { success: true, output: `Linked ${scope}: ${relation.from} ${relation.type} ${relation.to}` };
|
|
1989
2165
|
break;
|
|
1990
2166
|
}
|
|
1991
|
-
|
|
1992
|
-
const
|
|
1993
|
-
|
|
1994
|
-
|
|
1995
|
-
|
|
1996
|
-
|
|
2167
|
+
case 'search_context': {
|
|
2168
|
+
const scope = input.scope === 'org' ? 'org' : 'user';
|
|
2169
|
+
const graph = scope === 'org' ? this.orgContextGraph : this.contextGraph;
|
|
2170
|
+
if (!graph) {
|
|
2171
|
+
result = { success: false, output: 'Org context is not configured for this agent.' };
|
|
2172
|
+
break;
|
|
2173
|
+
}
|
|
2174
|
+
const entities = await graph.searchEntities(input.query, { kind: input.kind, limit: 6 });
|
|
2175
|
+
if (entities.length === 0) {
|
|
2176
|
+
result = { success: true, output: 'No context entities found.' };
|
|
2177
|
+
break;
|
|
2178
|
+
}
|
|
2179
|
+
const lines = entities.map(entity => {
|
|
2180
|
+
const desc = entity.description ? ` - ${entity.description}` : '';
|
|
2181
|
+
return `[${entity.kind}] ${entity.name}${desc} (${entity.key})`;
|
|
2182
|
+
});
|
|
2183
|
+
result = { success: true, output: lines.join('\n') };
|
|
2184
|
+
break;
|
|
2185
|
+
}
|
|
2186
|
+
case 'search_memory':
|
|
2187
|
+
result = await this.executeSearchMemory(input.query, input.type);
|
|
2188
|
+
break;
|
|
2189
|
+
case 'schedule_task':
|
|
2190
|
+
result = await this.executeScheduleTask(input.task, input.when);
|
|
2191
|
+
break;
|
|
2192
|
+
case 'list_scheduled':
|
|
2193
|
+
result = await this.executeListScheduled();
|
|
2194
|
+
break;
|
|
2195
|
+
case 'memory_stats':
|
|
2196
|
+
result = await this.executeMemoryStats();
|
|
2197
|
+
break;
|
|
2198
|
+
case 'open_browser':
|
|
2199
|
+
result = await this.executeOpenBrowser(input.url);
|
|
2200
|
+
break;
|
|
2201
|
+
case 'start_local_server':
|
|
2202
|
+
result = await this.executeStartServer(input.directory, input.port);
|
|
2203
|
+
break;
|
|
2204
|
+
case 'stop_local_server':
|
|
2205
|
+
result = await this.executeStopServer(input.port);
|
|
2206
|
+
break;
|
|
2207
|
+
// Lia's internal task management tools
|
|
2208
|
+
case 'lia_plan':
|
|
2209
|
+
result = this.executeLiaPlan(input.goal, input.steps);
|
|
2210
|
+
break;
|
|
2211
|
+
case 'lia_add_task':
|
|
2212
|
+
result = this.executeLiaAddTask(input.task, input.priority);
|
|
2213
|
+
break;
|
|
2214
|
+
case 'lia_get_queue':
|
|
2215
|
+
result = this.executeLiaGetQueue();
|
|
2216
|
+
break;
|
|
2217
|
+
case 'lia_todo_write':
|
|
2218
|
+
result = this.executeLiaTodoWrite(input.todos);
|
|
2219
|
+
break;
|
|
2220
|
+
default:
|
|
2221
|
+
result = { success: false, output: `Unknown tool: ${toolUse.name}` };
|
|
1997
2222
|
}
|
|
1998
|
-
case 'search_memory':
|
|
1999
|
-
result = await this.executeSearchMemory(input.query, input.type);
|
|
2000
|
-
break;
|
|
2001
|
-
case 'schedule_task':
|
|
2002
|
-
result = await this.executeScheduleTask(input.task, input.when);
|
|
2003
|
-
break;
|
|
2004
|
-
case 'list_scheduled':
|
|
2005
|
-
result = await this.executeListScheduled();
|
|
2006
|
-
break;
|
|
2007
|
-
case 'memory_stats':
|
|
2008
|
-
result = await this.executeMemoryStats();
|
|
2009
|
-
break;
|
|
2010
|
-
case 'open_browser':
|
|
2011
|
-
result = await this.executeOpenBrowser(input.url);
|
|
2012
|
-
break;
|
|
2013
|
-
case 'start_local_server':
|
|
2014
|
-
result = await this.executeStartServer(input.directory, input.port);
|
|
2015
|
-
break;
|
|
2016
|
-
case 'stop_local_server':
|
|
2017
|
-
result = await this.executeStopServer(input.port);
|
|
2018
|
-
break;
|
|
2019
|
-
// Lia's internal task management tools
|
|
2020
|
-
case 'lia_plan':
|
|
2021
|
-
result = this.executeLiaPlan(input.goal, input.steps);
|
|
2022
|
-
break;
|
|
2023
|
-
case 'lia_add_task':
|
|
2024
|
-
result = this.executeLiaAddTask(input.task, input.priority);
|
|
2025
|
-
break;
|
|
2026
|
-
case 'lia_get_queue':
|
|
2027
|
-
result = this.executeLiaGetQueue();
|
|
2028
|
-
break;
|
|
2029
|
-
case 'lia_todo_write':
|
|
2030
|
-
result = this.executeLiaTodoWrite(input.todos);
|
|
2031
|
-
break;
|
|
2032
|
-
default:
|
|
2033
|
-
result = { success: false, output: `Unknown tool: ${toolUse.name}` };
|
|
2034
|
-
}
|
|
2035
2223
|
// Anthropic API requires content to be non-empty when is_error is true
|
|
2036
2224
|
// Belt-and-suspenders: ensure content is NEVER empty
|
|
2037
2225
|
let content = result.output || '';
|
package/dist/tools/worker.d.ts
CHANGED
|
@@ -8,6 +8,7 @@ export { GalleryCallback, GalleryWorker };
|
|
|
8
8
|
export type { WorkerLogCallback };
|
|
9
9
|
export declare class WorkerTools {
|
|
10
10
|
private manager;
|
|
11
|
+
private workspaceDir;
|
|
11
12
|
constructor(workspaceDir: string);
|
|
12
13
|
/**
|
|
13
14
|
* Set callback for gallery updates (worker assets for UI)
|
package/dist/tools/worker.js
CHANGED
|
@@ -5,8 +5,10 @@
|
|
|
5
5
|
import { WorkerManager } from '../workers/manager.js';
|
|
6
6
|
export class WorkerTools {
|
|
7
7
|
manager;
|
|
8
|
+
workspaceDir;
|
|
8
9
|
constructor(workspaceDir) {
|
|
9
10
|
this.manager = new WorkerManager(workspaceDir);
|
|
11
|
+
this.workspaceDir = workspaceDir;
|
|
10
12
|
}
|
|
11
13
|
/**
|
|
12
14
|
* Set callback for gallery updates (worker assets for UI)
|
|
@@ -34,7 +36,7 @@ export class WorkerTools {
|
|
|
34
36
|
const jobId = await this.manager.spawn(task, {
|
|
35
37
|
timeout: options?.timeout,
|
|
36
38
|
priority: options?.priority,
|
|
37
|
-
workspace: options?.workingDirectory
|
|
39
|
+
workspace: options?.workingDirectory || this.workspaceDir
|
|
38
40
|
});
|
|
39
41
|
const job = this.manager.get(jobId);
|
|
40
42
|
return {
|
package/dist/websocket.d.ts
CHANGED
|
@@ -11,7 +11,7 @@ import { AssetResponse } from './core/asset-api.js';
|
|
|
11
11
|
export type MessageHandler = (message: WebSocketMessage) => Promise<void>;
|
|
12
12
|
export type StreamHandler = (chunk: string) => void;
|
|
13
13
|
export interface WebSocketMessage {
|
|
14
|
-
type: 'message' | 'interrupt' | 'ping' | 'pong' | 'connected' | 'asset_request';
|
|
14
|
+
type: 'message' | 'interrupt' | 'ping' | 'pong' | 'connected' | 'asset_request' | 'ready';
|
|
15
15
|
id?: string;
|
|
16
16
|
content?: string;
|
|
17
17
|
timestamp?: number;
|
|
@@ -37,6 +37,8 @@ export declare class AgentWebSocket {
|
|
|
37
37
|
private isConnected;
|
|
38
38
|
private pendingResponses;
|
|
39
39
|
private cloudflareBlocked;
|
|
40
|
+
private pendingMessages;
|
|
41
|
+
private readyPending;
|
|
40
42
|
constructor();
|
|
41
43
|
/**
|
|
42
44
|
* Connect to the WebSocket server
|
|
@@ -46,6 +48,7 @@ export declare class AgentWebSocket {
|
|
|
46
48
|
* Set handler for incoming messages
|
|
47
49
|
*/
|
|
48
50
|
onMessage(handler: MessageHandler): void;
|
|
51
|
+
sendReady(): void;
|
|
49
52
|
/**
|
|
50
53
|
* Send a streaming response for a message
|
|
51
54
|
*/
|
package/dist/websocket.js
CHANGED
|
@@ -21,6 +21,8 @@ export class AgentWebSocket {
|
|
|
21
21
|
isConnected = false;
|
|
22
22
|
pendingResponses = new Map();
|
|
23
23
|
cloudflareBlocked = false; // Track if Cloudflare is blocking WebSockets
|
|
24
|
+
pendingMessages = [];
|
|
25
|
+
readyPending = false;
|
|
24
26
|
constructor() {
|
|
25
27
|
const httpUrl = getServerUrl();
|
|
26
28
|
// Check for WebSocket-specific URL (bypasses Cloudflare)
|
|
@@ -51,6 +53,12 @@ export class AgentWebSocket {
|
|
|
51
53
|
this.isConnected = true;
|
|
52
54
|
this.reconnectAttempts = 0;
|
|
53
55
|
this.startPingInterval();
|
|
56
|
+
if (this.messageHandler) {
|
|
57
|
+
this.sendReady();
|
|
58
|
+
}
|
|
59
|
+
else {
|
|
60
|
+
this.readyPending = true;
|
|
61
|
+
}
|
|
54
62
|
resolve(true);
|
|
55
63
|
});
|
|
56
64
|
this.ws.on('message', (data) => {
|
|
@@ -99,6 +107,20 @@ export class AgentWebSocket {
|
|
|
99
107
|
*/
|
|
100
108
|
onMessage(handler) {
|
|
101
109
|
this.messageHandler = handler;
|
|
110
|
+
if (this.isConnected && this.readyPending) {
|
|
111
|
+
this.readyPending = false;
|
|
112
|
+
this.sendReady();
|
|
113
|
+
}
|
|
114
|
+
if (this.pendingMessages.length > 0) {
|
|
115
|
+
const queued = [...this.pendingMessages];
|
|
116
|
+
this.pendingMessages = [];
|
|
117
|
+
for (const message of queued) {
|
|
118
|
+
void handler(message);
|
|
119
|
+
}
|
|
120
|
+
}
|
|
121
|
+
}
|
|
122
|
+
sendReady() {
|
|
123
|
+
this.sendToServer({ type: 'ready' });
|
|
102
124
|
}
|
|
103
125
|
/**
|
|
104
126
|
* Send a streaming response for a message
|
|
@@ -251,6 +273,9 @@ export class AgentWebSocket {
|
|
|
251
273
|
if (this.messageHandler && message.content) {
|
|
252
274
|
this.messageHandler(message);
|
|
253
275
|
}
|
|
276
|
+
else if (message.content) {
|
|
277
|
+
this.pendingMessages.push(message);
|
|
278
|
+
}
|
|
254
279
|
break;
|
|
255
280
|
case 'asset_request':
|
|
256
281
|
// Handle asset requests (fast-path, no LLM)
|