@siftd/connect-agent 0.2.46 → 0.2.47
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 +7 -1
- package/dist/orchestrator.js +313 -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,12 @@ export declare class MasterOrchestrator {
|
|
|
193
194
|
private stripTodoSnapshot;
|
|
194
195
|
private hasTodoMutation;
|
|
195
196
|
private hasCalendarMutation;
|
|
196
|
-
private
|
|
197
|
+
private getUserTagHint;
|
|
198
|
+
private getBreakdownTarget;
|
|
199
|
+
private ensureBreakdownOriginalDone;
|
|
200
|
+
private getLocalDateKey;
|
|
201
|
+
private getLocalTimeZone;
|
|
202
|
+
private getTodoCalSystemPrompt;
|
|
197
203
|
private getToolChoice;
|
|
198
204
|
private withAttachments;
|
|
199
205
|
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,66 @@ 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
|
+
getUserTagHint(message) {
|
|
705
|
+
if (!message)
|
|
706
|
+
return null;
|
|
707
|
+
const base = this.stripTodoSnapshot(message);
|
|
708
|
+
const matches = base.match(/\[[^\]]{2,64}\]/g) || [];
|
|
709
|
+
const unique = Array.from(new Set(matches.map((tag) => tag.trim()))).filter(Boolean);
|
|
710
|
+
if (unique.length !== 1)
|
|
711
|
+
return null;
|
|
712
|
+
return unique[0];
|
|
713
|
+
}
|
|
714
|
+
getBreakdownTarget(message) {
|
|
715
|
+
if (!message)
|
|
716
|
+
return null;
|
|
717
|
+
const stripped = this.stripTodoSnapshot(message);
|
|
718
|
+
if (!/\b(break(?:\s|-)?down|split|parse)\b/i.test(stripped))
|
|
719
|
+
return null;
|
|
720
|
+
const quoted = stripped.match(/["“]([^"”]{3,160})["”]/);
|
|
721
|
+
if (quoted && quoted[1]) {
|
|
722
|
+
return quoted[1].trim();
|
|
723
|
+
}
|
|
724
|
+
const inline = stripped.match(/break(?:\s|-)?down\s+the\s+(.+?)\s+(?:item|todo|task)\b/i);
|
|
725
|
+
if (inline && inline[1]) {
|
|
726
|
+
return inline[1].trim();
|
|
727
|
+
}
|
|
728
|
+
return null;
|
|
729
|
+
}
|
|
730
|
+
ensureBreakdownOriginalDone(items, targetTitle) {
|
|
731
|
+
const normalizedTarget = targetTitle.trim().toLowerCase();
|
|
732
|
+
let found = false;
|
|
733
|
+
const nextItems = items.map((item) => {
|
|
734
|
+
if (item?.title && item.title.trim().toLowerCase() === normalizedTarget) {
|
|
735
|
+
found = true;
|
|
736
|
+
return { ...item, done: true };
|
|
737
|
+
}
|
|
738
|
+
return item;
|
|
739
|
+
});
|
|
740
|
+
if (!found) {
|
|
741
|
+
nextItems.unshift({ title: targetTitle, done: true });
|
|
742
|
+
}
|
|
743
|
+
return nextItems;
|
|
744
|
+
}
|
|
745
|
+
getLocalDateKey(date = new Date()) {
|
|
746
|
+
const year = date.getFullYear();
|
|
747
|
+
const month = `${date.getMonth() + 1}`.padStart(2, '0');
|
|
748
|
+
const day = `${date.getDate()}`.padStart(2, '0');
|
|
749
|
+
return `${year}-${month}-${day}`;
|
|
750
|
+
}
|
|
751
|
+
getLocalTimeZone() {
|
|
752
|
+
try {
|
|
753
|
+
return Intl.DateTimeFormat().resolvedOptions().timeZone || null;
|
|
754
|
+
}
|
|
755
|
+
catch {
|
|
756
|
+
return null;
|
|
757
|
+
}
|
|
758
|
+
}
|
|
759
|
+
getTodoCalSystemPrompt() {
|
|
760
|
+
const today = this.getLocalDateKey();
|
|
761
|
+
const timeZone = this.getLocalTimeZone();
|
|
762
|
+
const tzNote = timeZone ? ` (${timeZone})` : '';
|
|
763
|
+
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
764
|
}
|
|
694
765
|
getToolChoice(messages) {
|
|
695
766
|
const last = messages[messages.length - 1];
|
|
@@ -700,7 +771,7 @@ export class MasterOrchestrator {
|
|
|
700
771
|
if (wantsTodo && !wantsCal) {
|
|
701
772
|
return { type: 'tool', name: 'todo_upsert_items' };
|
|
702
773
|
}
|
|
703
|
-
if (wantsCal && !wantsTodo
|
|
774
|
+
if (wantsCal && !wantsTodo) {
|
|
704
775
|
return { type: 'tool', name: 'calendar_upsert_events' };
|
|
705
776
|
}
|
|
706
777
|
return undefined;
|
|
@@ -775,12 +846,28 @@ export class MasterOrchestrator {
|
|
|
775
846
|
* Process a user message
|
|
776
847
|
*/
|
|
777
848
|
async processMessage(message, conversationHistory = [], sendMessage) {
|
|
849
|
+
this.lastUserMessage = message;
|
|
778
850
|
// Handle slash commands first
|
|
779
851
|
const slashResponse = this.handleSlashCommand(message);
|
|
780
852
|
if (slashResponse) {
|
|
781
853
|
return slashResponse;
|
|
782
854
|
}
|
|
783
855
|
this.updateFileScope(message);
|
|
856
|
+
const wantsTodoOrCal = this.hasTodoMutation(message) || this.hasCalendarMutation(message);
|
|
857
|
+
if (wantsTodoOrCal) {
|
|
858
|
+
this.attachmentContext = this.extractAttachmentContext(message);
|
|
859
|
+
const toolMessages = [{ role: 'user', content: message }];
|
|
860
|
+
try {
|
|
861
|
+
return await this.runAgentLoop(toolMessages, this.getTodoCalSystemPrompt(), sendMessage);
|
|
862
|
+
}
|
|
863
|
+
catch (error) {
|
|
864
|
+
const errorMessage = error instanceof Error ? error.message : String(error);
|
|
865
|
+
return `Error: ${errorMessage}`;
|
|
866
|
+
}
|
|
867
|
+
finally {
|
|
868
|
+
this.attachmentContext = null;
|
|
869
|
+
}
|
|
870
|
+
}
|
|
784
871
|
// DISABLED: Dumb regex extraction was creating garbage todos
|
|
785
872
|
// Let the AI use calendar_upsert_events and todo_upsert_items tools properly
|
|
786
873
|
// const quickWrite = this.tryHandleCalendarTodo(message);
|
|
@@ -1231,14 +1318,34 @@ ${hubContextStr}
|
|
|
1231
1318
|
* Run the agentic loop
|
|
1232
1319
|
*/
|
|
1233
1320
|
async runAgentLoop(messages, system, sendMessage) {
|
|
1234
|
-
const
|
|
1321
|
+
const toolsBase = this.getToolDefinitions();
|
|
1322
|
+
const last = messages[messages.length - 1];
|
|
1323
|
+
const lastContent = last && last.role === 'user' && typeof last.content === 'string' ? last.content : '';
|
|
1324
|
+
const restrictWorkers = lastContent
|
|
1325
|
+
? (this.hasTodoMutation(lastContent) || this.hasCalendarMutation(lastContent))
|
|
1326
|
+
: false;
|
|
1327
|
+
const wantsTodoOrCal = restrictWorkers;
|
|
1328
|
+
const todoCalTools = new Set(['calendar_upsert_events', 'todo_upsert_items']);
|
|
1329
|
+
const blockedTools = new Set([
|
|
1330
|
+
'spawn_worker',
|
|
1331
|
+
'delegate_to_worker',
|
|
1332
|
+
'check_worker',
|
|
1333
|
+
'wait_worker',
|
|
1334
|
+
'list_workers',
|
|
1335
|
+
'cancel_worker',
|
|
1336
|
+
]);
|
|
1337
|
+
const tools = wantsTodoOrCal
|
|
1338
|
+
? toolsBase.filter((tool) => todoCalTools.has(tool.name))
|
|
1339
|
+
: toolsBase.filter((tool) => !blockedTools.has(tool.name) || !restrictWorkers);
|
|
1235
1340
|
let currentMessages = [...messages];
|
|
1236
1341
|
let iterations = 0;
|
|
1237
1342
|
const maxIterations = 30; // Increased for complex multi-tool tasks
|
|
1238
1343
|
const requestTimeoutMs = 60000;
|
|
1344
|
+
const forcedToolChoice = this.getToolChoice(currentMessages);
|
|
1345
|
+
let retriedForcedTool = false;
|
|
1239
1346
|
while (iterations < maxIterations) {
|
|
1240
1347
|
iterations++;
|
|
1241
|
-
const toolChoice = this.getToolChoice(currentMessages);
|
|
1348
|
+
const toolChoice = forcedToolChoice ?? this.getToolChoice(currentMessages);
|
|
1242
1349
|
const requestStart = Date.now();
|
|
1243
1350
|
console.log(`[ORCHESTRATOR] Anthropic request ${iterations}/${maxIterations} (model: ${this.model})`);
|
|
1244
1351
|
const response = await Promise.race([
|
|
@@ -1259,10 +1366,32 @@ ${hubContextStr}
|
|
|
1259
1366
|
console.log(`[ORCHESTRATOR] Anthropic response ${iterations}/${maxIterations} in ${Date.now() - requestStart}ms`);
|
|
1260
1367
|
// Check if done
|
|
1261
1368
|
if (response.stop_reason === 'end_turn' || !this.hasToolUse(response.content)) {
|
|
1369
|
+
if (forcedToolChoice && !retriedForcedTool) {
|
|
1370
|
+
retriedForcedTool = true;
|
|
1371
|
+
currentMessages = [
|
|
1372
|
+
...currentMessages,
|
|
1373
|
+
{ role: 'assistant', content: response.content },
|
|
1374
|
+
{
|
|
1375
|
+
role: 'user',
|
|
1376
|
+
content: `You must call the ${forcedToolChoice.name} tool now. Use the exact task/event titles and any bracketed tags exactly as provided. Do not spawn workers.`
|
|
1377
|
+
}
|
|
1378
|
+
];
|
|
1379
|
+
continue;
|
|
1380
|
+
}
|
|
1262
1381
|
return this.extractText(response.content);
|
|
1263
1382
|
}
|
|
1383
|
+
const toolUseBlocks = response.content.filter((block) => block.type === 'tool_use');
|
|
1384
|
+
const toolNames = toolUseBlocks.map((block) => block.name);
|
|
1264
1385
|
// Process tool calls
|
|
1265
1386
|
const toolResults = await this.processToolCalls(response.content, sendMessage);
|
|
1387
|
+
if (wantsTodoOrCal && (toolNames.includes('todo_upsert_items') || toolNames.includes('calendar_upsert_events'))) {
|
|
1388
|
+
const updates = [];
|
|
1389
|
+
if (toolNames.includes('todo_upsert_items'))
|
|
1390
|
+
updates.push('Updated /todo.');
|
|
1391
|
+
if (toolNames.includes('calendar_upsert_events'))
|
|
1392
|
+
updates.push('Updated /cal.');
|
|
1393
|
+
return `${updates.join(' ')} Open /todo or /cal to view.`;
|
|
1394
|
+
}
|
|
1266
1395
|
// Continue conversation
|
|
1267
1396
|
currentMessages = [
|
|
1268
1397
|
...currentMessages,
|
|
@@ -1878,6 +2007,17 @@ Unlike lia_plan (internal only), this creates a VISIBLE todo list that appears i
|
|
|
1878
2007
|
async processToolCalls(content, sendMessage) {
|
|
1879
2008
|
const toolUseBlocks = content.filter((block) => block.type === 'tool_use');
|
|
1880
2009
|
const results = [];
|
|
2010
|
+
const wantsTodoOrCal = this.lastUserMessage
|
|
2011
|
+
? (this.hasTodoMutation(this.lastUserMessage) || this.hasCalendarMutation(this.lastUserMessage))
|
|
2012
|
+
: false;
|
|
2013
|
+
const blockedWorkerTools = new Set([
|
|
2014
|
+
'spawn_worker',
|
|
2015
|
+
'delegate_to_worker',
|
|
2016
|
+
'check_worker',
|
|
2017
|
+
'wait_worker',
|
|
2018
|
+
'list_workers',
|
|
2019
|
+
'cancel_worker',
|
|
2020
|
+
]);
|
|
1881
2021
|
for (const toolUse of toolUseBlocks) {
|
|
1882
2022
|
const input = toolUse.input;
|
|
1883
2023
|
let result;
|
|
@@ -1887,151 +2027,183 @@ Unlike lia_plan (internal only), this creates a VISIBLE todo list that appears i
|
|
|
1887
2027
|
if (preview)
|
|
1888
2028
|
await sendMessage(preview);
|
|
1889
2029
|
}
|
|
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
|
-
|
|
2030
|
+
if (wantsTodoOrCal && blockedWorkerTools.has(toolUse.name)) {
|
|
2031
|
+
result = {
|
|
2032
|
+
success: false,
|
|
2033
|
+
output: '',
|
|
2034
|
+
error: 'Do not use worker tools for /todo or /cal. Call todo_upsert_items or calendar_upsert_events directly.'
|
|
2035
|
+
};
|
|
2036
|
+
}
|
|
2037
|
+
else
|
|
2038
|
+
switch (toolUse.name) {
|
|
2039
|
+
// New direct tools
|
|
2040
|
+
case 'bash':
|
|
2041
|
+
result = await this.bashTool.execute(input.command, input.timeout);
|
|
2042
|
+
break;
|
|
2043
|
+
case 'calendar_upsert_events':
|
|
2044
|
+
{
|
|
2045
|
+
const tagHint = this.getUserTagHint(this.lastUserMessage);
|
|
2046
|
+
const events = input.events || [];
|
|
2047
|
+
const taggedEvents = tagHint
|
|
2048
|
+
? events.map((event) => ({
|
|
2049
|
+
...event,
|
|
2050
|
+
title: event.title.includes(tagHint) ? event.title : `${event.title} ${tagHint}`
|
|
2051
|
+
}))
|
|
2052
|
+
: events;
|
|
2053
|
+
result = this.calendarTools.upsertCalendarEvents(taggedEvents, input.calendars);
|
|
2054
|
+
}
|
|
2055
|
+
break;
|
|
2056
|
+
case 'todo_upsert_items':
|
|
2057
|
+
{
|
|
2058
|
+
const tagHint = this.getUserTagHint(this.lastUserMessage);
|
|
2059
|
+
let items = input.items || [];
|
|
2060
|
+
const breakdownTarget = this.getBreakdownTarget(this.lastUserMessage);
|
|
2061
|
+
if (breakdownTarget) {
|
|
2062
|
+
items = this.ensureBreakdownOriginalDone(items, breakdownTarget);
|
|
2063
|
+
}
|
|
2064
|
+
const taggedItems = tagHint
|
|
2065
|
+
? items.map((item) => ({
|
|
2066
|
+
...item,
|
|
2067
|
+
title: item.title.includes(tagHint) ? item.title : `${item.title} ${tagHint}`
|
|
2068
|
+
}))
|
|
2069
|
+
: items;
|
|
2070
|
+
result = this.calendarTools.upsertTodoItems(taggedItems);
|
|
2071
|
+
}
|
|
2072
|
+
break;
|
|
2073
|
+
case 'web_search':
|
|
2074
|
+
result = await this.webTools.webSearch(input.query, { numResults: input.num_results });
|
|
2075
|
+
break;
|
|
2076
|
+
case 'fetch_url':
|
|
2077
|
+
result = await this.webTools.fetchUrl(input.url, {
|
|
2078
|
+
format: input.format,
|
|
2079
|
+
timeout: input.timeout
|
|
2080
|
+
});
|
|
2081
|
+
break;
|
|
2082
|
+
// Worker tools
|
|
2083
|
+
case 'spawn_worker': {
|
|
2084
|
+
const { task } = this.withAttachments(input.task);
|
|
2085
|
+
const fileScope = this.getFileScopeOverrides();
|
|
2086
|
+
const scopedTask = fileScope.instructions ? `${fileScope.instructions}\n\n${task}` : task;
|
|
2087
|
+
result = await this.workerTools.spawnWorker(scopedTask, {
|
|
2088
|
+
timeout: input.timeout,
|
|
2089
|
+
priority: input.priority,
|
|
2090
|
+
workingDirectory: input.working_directory || fileScope.workingDir
|
|
2091
|
+
});
|
|
1949
2092
|
break;
|
|
1950
2093
|
}
|
|
1951
|
-
|
|
1952
|
-
|
|
1953
|
-
|
|
1954
|
-
|
|
1955
|
-
|
|
1956
|
-
|
|
1957
|
-
|
|
1958
|
-
|
|
1959
|
-
|
|
1960
|
-
|
|
1961
|
-
|
|
1962
|
-
|
|
1963
|
-
|
|
1964
|
-
|
|
1965
|
-
|
|
2094
|
+
case 'check_worker':
|
|
2095
|
+
result = await this.workerTools.checkWorker(input.job_id);
|
|
2096
|
+
break;
|
|
2097
|
+
case 'wait_worker':
|
|
2098
|
+
result = await this.workerTools.waitWorker(input.job_id, input.max_wait);
|
|
2099
|
+
break;
|
|
2100
|
+
case 'list_workers':
|
|
2101
|
+
result = await this.workerTools.listWorkers(input.status);
|
|
2102
|
+
break;
|
|
2103
|
+
case 'cancel_worker':
|
|
2104
|
+
result = await this.workerTools.cancelWorker(input.job_id);
|
|
2105
|
+
break;
|
|
2106
|
+
// Legacy delegate tool
|
|
2107
|
+
case 'delegate_to_worker': {
|
|
2108
|
+
const { task, context } = this.withAttachments(input.task, input.context);
|
|
2109
|
+
const fileScope = this.getFileScopeOverrides();
|
|
2110
|
+
result = await this.delegateToWorker(task, context, input.working_directory || fileScope.workingDir, fileScope.instructions);
|
|
1966
2111
|
break;
|
|
1967
2112
|
}
|
|
1968
|
-
|
|
1969
|
-
|
|
1970
|
-
|
|
1971
|
-
|
|
1972
|
-
|
|
1973
|
-
|
|
1974
|
-
|
|
1975
|
-
|
|
1976
|
-
|
|
1977
|
-
|
|
1978
|
-
|
|
1979
|
-
|
|
1980
|
-
|
|
1981
|
-
|
|
1982
|
-
|
|
1983
|
-
|
|
2113
|
+
case 'remember':
|
|
2114
|
+
result = await this.executeRemember(input.content, input.type, input.importance);
|
|
2115
|
+
break;
|
|
2116
|
+
case 'remember_entity': {
|
|
2117
|
+
const scope = input.scope === 'org' ? 'org' : 'user';
|
|
2118
|
+
const graph = scope === 'org' ? this.orgContextGraph : this.contextGraph;
|
|
2119
|
+
if (!graph) {
|
|
2120
|
+
result = { success: false, output: 'Org context is not configured for this agent.' };
|
|
2121
|
+
break;
|
|
2122
|
+
}
|
|
2123
|
+
const entity = await graph.upsertEntity({
|
|
2124
|
+
kind: input.kind,
|
|
2125
|
+
name: input.name,
|
|
2126
|
+
description: input.description,
|
|
2127
|
+
attributes: input.attributes,
|
|
2128
|
+
tags: input.tags
|
|
2129
|
+
});
|
|
2130
|
+
result = { success: true, output: `Stored ${scope} entity: ${entity.kind} ${entity.name} (${entity.key})` };
|
|
1984
2131
|
break;
|
|
1985
2132
|
}
|
|
1986
|
-
|
|
1987
|
-
|
|
1988
|
-
|
|
2133
|
+
case 'remember_relation': {
|
|
2134
|
+
const scope = input.scope === 'org' ? 'org' : 'user';
|
|
2135
|
+
const graph = scope === 'org' ? this.orgContextGraph : this.contextGraph;
|
|
2136
|
+
if (!graph) {
|
|
2137
|
+
result = { success: false, output: 'Org context is not configured for this agent.' };
|
|
2138
|
+
break;
|
|
2139
|
+
}
|
|
2140
|
+
const relation = await graph.linkEntities({
|
|
2141
|
+
from: input.from,
|
|
2142
|
+
to: input.to,
|
|
2143
|
+
type: input.type,
|
|
2144
|
+
description: input.description,
|
|
2145
|
+
fromKind: input.from_kind,
|
|
2146
|
+
toKind: input.to_kind
|
|
2147
|
+
});
|
|
2148
|
+
result = { success: true, output: `Linked ${scope}: ${relation.from} ${relation.type} ${relation.to}` };
|
|
1989
2149
|
break;
|
|
1990
2150
|
}
|
|
1991
|
-
|
|
1992
|
-
const
|
|
1993
|
-
|
|
1994
|
-
|
|
1995
|
-
|
|
1996
|
-
|
|
2151
|
+
case 'search_context': {
|
|
2152
|
+
const scope = input.scope === 'org' ? 'org' : 'user';
|
|
2153
|
+
const graph = scope === 'org' ? this.orgContextGraph : this.contextGraph;
|
|
2154
|
+
if (!graph) {
|
|
2155
|
+
result = { success: false, output: 'Org context is not configured for this agent.' };
|
|
2156
|
+
break;
|
|
2157
|
+
}
|
|
2158
|
+
const entities = await graph.searchEntities(input.query, { kind: input.kind, limit: 6 });
|
|
2159
|
+
if (entities.length === 0) {
|
|
2160
|
+
result = { success: true, output: 'No context entities found.' };
|
|
2161
|
+
break;
|
|
2162
|
+
}
|
|
2163
|
+
const lines = entities.map(entity => {
|
|
2164
|
+
const desc = entity.description ? ` - ${entity.description}` : '';
|
|
2165
|
+
return `[${entity.kind}] ${entity.name}${desc} (${entity.key})`;
|
|
2166
|
+
});
|
|
2167
|
+
result = { success: true, output: lines.join('\n') };
|
|
2168
|
+
break;
|
|
2169
|
+
}
|
|
2170
|
+
case 'search_memory':
|
|
2171
|
+
result = await this.executeSearchMemory(input.query, input.type);
|
|
2172
|
+
break;
|
|
2173
|
+
case 'schedule_task':
|
|
2174
|
+
result = await this.executeScheduleTask(input.task, input.when);
|
|
2175
|
+
break;
|
|
2176
|
+
case 'list_scheduled':
|
|
2177
|
+
result = await this.executeListScheduled();
|
|
2178
|
+
break;
|
|
2179
|
+
case 'memory_stats':
|
|
2180
|
+
result = await this.executeMemoryStats();
|
|
2181
|
+
break;
|
|
2182
|
+
case 'open_browser':
|
|
2183
|
+
result = await this.executeOpenBrowser(input.url);
|
|
2184
|
+
break;
|
|
2185
|
+
case 'start_local_server':
|
|
2186
|
+
result = await this.executeStartServer(input.directory, input.port);
|
|
2187
|
+
break;
|
|
2188
|
+
case 'stop_local_server':
|
|
2189
|
+
result = await this.executeStopServer(input.port);
|
|
2190
|
+
break;
|
|
2191
|
+
// Lia's internal task management tools
|
|
2192
|
+
case 'lia_plan':
|
|
2193
|
+
result = this.executeLiaPlan(input.goal, input.steps);
|
|
2194
|
+
break;
|
|
2195
|
+
case 'lia_add_task':
|
|
2196
|
+
result = this.executeLiaAddTask(input.task, input.priority);
|
|
2197
|
+
break;
|
|
2198
|
+
case 'lia_get_queue':
|
|
2199
|
+
result = this.executeLiaGetQueue();
|
|
2200
|
+
break;
|
|
2201
|
+
case 'lia_todo_write':
|
|
2202
|
+
result = this.executeLiaTodoWrite(input.todos);
|
|
2203
|
+
break;
|
|
2204
|
+
default:
|
|
2205
|
+
result = { success: false, output: `Unknown tool: ${toolUse.name}` };
|
|
1997
2206
|
}
|
|
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
2207
|
// Anthropic API requires content to be non-empty when is_error is true
|
|
2036
2208
|
// Belt-and-suspenders: ensure content is NEVER empty
|
|
2037
2209
|
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)
|