@siftd/connect-agent 0.2.37 → 0.2.39
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/agent.js +173 -13
- package/dist/api.d.ts +2 -0
- package/dist/cli.js +4 -1
- package/dist/config.d.ts +7 -0
- package/dist/config.js +41 -0
- package/dist/core/assets.js +12 -1
- package/dist/core/context-graph.d.ts +94 -0
- package/dist/core/context-graph.js +247 -0
- package/dist/core/file-tracker.js +3 -1
- package/dist/core/hub.js +36 -1
- package/dist/core/memory-advanced.d.ts +5 -0
- package/dist/core/memory-advanced.js +27 -0
- package/dist/core/memory-postgres.d.ts +9 -0
- package/dist/core/memory-postgres.js +48 -0
- package/dist/core/preview-worker.js +3 -0
- package/dist/core/task-queue.d.ts +125 -0
- package/dist/core/task-queue.js +248 -0
- package/dist/index.d.ts +1 -0
- package/dist/index.js +1 -0
- package/dist/orchestrator.d.ts +108 -0
- package/dist/orchestrator.js +1086 -26
- package/dist/tools/calendar.d.ts +50 -0
- package/dist/tools/calendar.js +233 -0
- package/dist/tools/worker.d.ts +6 -1
- package/dist/tools/worker.js +6 -0
- package/dist/workers/manager.d.ts +7 -0
- package/dist/workers/manager.js +35 -5
- package/package.json +1 -1
|
@@ -0,0 +1,248 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Lia's Internal Task Queue
|
|
3
|
+
*
|
|
4
|
+
* This manages Lia's own todo list - separate from user /todos.
|
|
5
|
+
* Allows async message processing where users can keep sending
|
|
6
|
+
* while Lia works through her queue.
|
|
7
|
+
*/
|
|
8
|
+
/**
|
|
9
|
+
* Lia's task queue - manages her internal todo list
|
|
10
|
+
*/
|
|
11
|
+
export class LiaTaskQueue {
|
|
12
|
+
tasks = new Map();
|
|
13
|
+
plans = new Map();
|
|
14
|
+
queue = []; // Task IDs in priority order
|
|
15
|
+
currentTask = null;
|
|
16
|
+
isProcessing = false;
|
|
17
|
+
// Callbacks for real-time updates
|
|
18
|
+
onTaskUpdate;
|
|
19
|
+
onPlanUpdate;
|
|
20
|
+
processTask;
|
|
21
|
+
constructor(options) {
|
|
22
|
+
this.onTaskUpdate = options?.onTaskUpdate;
|
|
23
|
+
this.onPlanUpdate = options?.onPlanUpdate;
|
|
24
|
+
this.processTask = options?.processTask;
|
|
25
|
+
}
|
|
26
|
+
/**
|
|
27
|
+
* Add a new task to the queue
|
|
28
|
+
*/
|
|
29
|
+
addTask(task) {
|
|
30
|
+
const newTask = {
|
|
31
|
+
...task,
|
|
32
|
+
id: `task_${Date.now()}_${Math.random().toString(36).slice(2, 8)}`,
|
|
33
|
+
status: 'pending',
|
|
34
|
+
createdAt: new Date(),
|
|
35
|
+
};
|
|
36
|
+
this.tasks.set(newTask.id, newTask);
|
|
37
|
+
this.insertByPriority(newTask.id, newTask.priority);
|
|
38
|
+
this.onTaskUpdate?.(newTask);
|
|
39
|
+
// Auto-start processing if not already running
|
|
40
|
+
if (!this.isProcessing) {
|
|
41
|
+
this.startProcessing();
|
|
42
|
+
}
|
|
43
|
+
return newTask;
|
|
44
|
+
}
|
|
45
|
+
/**
|
|
46
|
+
* Insert task ID into queue based on priority
|
|
47
|
+
*/
|
|
48
|
+
insertByPriority(taskId, priority) {
|
|
49
|
+
const priorityOrder = {
|
|
50
|
+
urgent: 0,
|
|
51
|
+
high: 1,
|
|
52
|
+
normal: 2,
|
|
53
|
+
low: 3,
|
|
54
|
+
};
|
|
55
|
+
const taskPriority = priorityOrder[priority];
|
|
56
|
+
let insertIndex = this.queue.length;
|
|
57
|
+
for (let i = 0; i < this.queue.length; i++) {
|
|
58
|
+
const existingTask = this.tasks.get(this.queue[i]);
|
|
59
|
+
if (existingTask && priorityOrder[existingTask.priority] > taskPriority) {
|
|
60
|
+
insertIndex = i;
|
|
61
|
+
break;
|
|
62
|
+
}
|
|
63
|
+
}
|
|
64
|
+
this.queue.splice(insertIndex, 0, taskId);
|
|
65
|
+
}
|
|
66
|
+
/**
|
|
67
|
+
* Start processing the queue
|
|
68
|
+
*/
|
|
69
|
+
async startProcessing() {
|
|
70
|
+
if (this.isProcessing)
|
|
71
|
+
return;
|
|
72
|
+
this.isProcessing = true;
|
|
73
|
+
while (this.queue.length > 0) {
|
|
74
|
+
const taskId = this.queue.shift();
|
|
75
|
+
if (!taskId)
|
|
76
|
+
continue;
|
|
77
|
+
const task = this.tasks.get(taskId);
|
|
78
|
+
if (!task || task.status === 'cancelled')
|
|
79
|
+
continue;
|
|
80
|
+
this.currentTask = taskId;
|
|
81
|
+
task.status = 'in_progress';
|
|
82
|
+
task.startedAt = new Date();
|
|
83
|
+
this.onTaskUpdate?.(task);
|
|
84
|
+
try {
|
|
85
|
+
if (this.processTask) {
|
|
86
|
+
const result = await this.processTask(task);
|
|
87
|
+
task.result = result;
|
|
88
|
+
task.status = 'completed';
|
|
89
|
+
}
|
|
90
|
+
else {
|
|
91
|
+
task.status = 'failed';
|
|
92
|
+
task.error = 'No task processor configured';
|
|
93
|
+
}
|
|
94
|
+
}
|
|
95
|
+
catch (error) {
|
|
96
|
+
task.status = 'failed';
|
|
97
|
+
task.error = error instanceof Error ? error.message : String(error);
|
|
98
|
+
}
|
|
99
|
+
task.completedAt = new Date();
|
|
100
|
+
this.currentTask = null;
|
|
101
|
+
this.onTaskUpdate?.(task);
|
|
102
|
+
}
|
|
103
|
+
this.isProcessing = false;
|
|
104
|
+
}
|
|
105
|
+
/**
|
|
106
|
+
* Get the current task being processed
|
|
107
|
+
*/
|
|
108
|
+
getCurrentTask() {
|
|
109
|
+
return this.currentTask ? this.tasks.get(this.currentTask) || null : null;
|
|
110
|
+
}
|
|
111
|
+
/**
|
|
112
|
+
* Get all pending tasks
|
|
113
|
+
*/
|
|
114
|
+
getPendingTasks() {
|
|
115
|
+
return this.queue
|
|
116
|
+
.map(id => this.tasks.get(id))
|
|
117
|
+
.filter((t) => t !== undefined && t.status === 'pending');
|
|
118
|
+
}
|
|
119
|
+
/**
|
|
120
|
+
* Get recent tasks (last N)
|
|
121
|
+
*/
|
|
122
|
+
getRecentTasks(limit = 10) {
|
|
123
|
+
return Array.from(this.tasks.values())
|
|
124
|
+
.sort((a, b) => b.createdAt.getTime() - a.createdAt.getTime())
|
|
125
|
+
.slice(0, limit);
|
|
126
|
+
}
|
|
127
|
+
/**
|
|
128
|
+
* Get task by ID
|
|
129
|
+
*/
|
|
130
|
+
getTask(taskId) {
|
|
131
|
+
return this.tasks.get(taskId) || null;
|
|
132
|
+
}
|
|
133
|
+
/**
|
|
134
|
+
* Cancel a pending task
|
|
135
|
+
*/
|
|
136
|
+
cancelTask(taskId) {
|
|
137
|
+
const task = this.tasks.get(taskId);
|
|
138
|
+
if (!task || task.status !== 'pending')
|
|
139
|
+
return false;
|
|
140
|
+
task.status = 'cancelled';
|
|
141
|
+
this.queue = this.queue.filter(id => id !== taskId);
|
|
142
|
+
this.onTaskUpdate?.(task);
|
|
143
|
+
return true;
|
|
144
|
+
}
|
|
145
|
+
/**
|
|
146
|
+
* Create a plan (breaks down a goal into steps)
|
|
147
|
+
*/
|
|
148
|
+
createPlan(goal, steps) {
|
|
149
|
+
const plan = {
|
|
150
|
+
id: `plan_${Date.now()}_${Math.random().toString(36).slice(2, 8)}`,
|
|
151
|
+
goal,
|
|
152
|
+
steps: steps.map((desc, i) => ({
|
|
153
|
+
id: `step_${i + 1}`,
|
|
154
|
+
description: desc,
|
|
155
|
+
status: 'pending',
|
|
156
|
+
})),
|
|
157
|
+
status: 'planning',
|
|
158
|
+
createdAt: new Date(),
|
|
159
|
+
};
|
|
160
|
+
this.plans.set(plan.id, plan);
|
|
161
|
+
this.onPlanUpdate?.(plan);
|
|
162
|
+
return plan;
|
|
163
|
+
}
|
|
164
|
+
/**
|
|
165
|
+
* Update a plan step
|
|
166
|
+
*/
|
|
167
|
+
updatePlanStep(planId, stepId, update) {
|
|
168
|
+
const plan = this.plans.get(planId);
|
|
169
|
+
if (!plan)
|
|
170
|
+
return false;
|
|
171
|
+
const step = plan.steps.find(s => s.id === stepId);
|
|
172
|
+
if (!step)
|
|
173
|
+
return false;
|
|
174
|
+
Object.assign(step, update);
|
|
175
|
+
// Update plan status based on steps
|
|
176
|
+
const allCompleted = plan.steps.every(s => s.status === 'completed');
|
|
177
|
+
const anyFailed = plan.steps.some(s => s.status === 'failed');
|
|
178
|
+
const anyInProgress = plan.steps.some(s => s.status === 'in_progress');
|
|
179
|
+
if (allCompleted) {
|
|
180
|
+
plan.status = 'completed';
|
|
181
|
+
plan.completedAt = new Date();
|
|
182
|
+
}
|
|
183
|
+
else if (anyFailed) {
|
|
184
|
+
plan.status = 'failed';
|
|
185
|
+
}
|
|
186
|
+
else if (anyInProgress) {
|
|
187
|
+
plan.status = 'executing';
|
|
188
|
+
}
|
|
189
|
+
this.onPlanUpdate?.(plan);
|
|
190
|
+
return true;
|
|
191
|
+
}
|
|
192
|
+
/**
|
|
193
|
+
* Get current plan
|
|
194
|
+
*/
|
|
195
|
+
getCurrentPlan() {
|
|
196
|
+
return Array.from(this.plans.values())
|
|
197
|
+
.find(p => p.status === 'planning' || p.status === 'executing') || null;
|
|
198
|
+
}
|
|
199
|
+
/**
|
|
200
|
+
* Get all plans
|
|
201
|
+
*/
|
|
202
|
+
getPlans() {
|
|
203
|
+
return Array.from(this.plans.values());
|
|
204
|
+
}
|
|
205
|
+
/**
|
|
206
|
+
* Get queue status for display
|
|
207
|
+
*/
|
|
208
|
+
getStatus() {
|
|
209
|
+
const pending = this.getPendingTasks();
|
|
210
|
+
return {
|
|
211
|
+
isProcessing: this.isProcessing,
|
|
212
|
+
currentTask: this.getCurrentTask(),
|
|
213
|
+
pendingCount: pending.length,
|
|
214
|
+
queuePreview: pending.slice(0, 5).map(t => t.content.length > 50 ? t.content.slice(0, 50) + '...' : t.content),
|
|
215
|
+
};
|
|
216
|
+
}
|
|
217
|
+
/**
|
|
218
|
+
* Format tasks as readable todo list (for LLM context)
|
|
219
|
+
*/
|
|
220
|
+
formatAsTodoList() {
|
|
221
|
+
const current = this.getCurrentTask();
|
|
222
|
+
const pending = this.getPendingTasks();
|
|
223
|
+
const recent = this.getRecentTasks(5).filter(t => t.status === 'completed' || t.status === 'failed');
|
|
224
|
+
const lines = ['## My Task Queue'];
|
|
225
|
+
if (current) {
|
|
226
|
+
lines.push('', '### Currently Working On:');
|
|
227
|
+
lines.push(`- [→] ${current.content}`);
|
|
228
|
+
}
|
|
229
|
+
if (pending.length > 0) {
|
|
230
|
+
lines.push('', '### Pending:');
|
|
231
|
+
for (const task of pending.slice(0, 10)) {
|
|
232
|
+
const priority = task.priority !== 'normal' ? ` [${task.priority}]` : '';
|
|
233
|
+
lines.push(`- [ ] ${task.content}${priority}`);
|
|
234
|
+
}
|
|
235
|
+
if (pending.length > 10) {
|
|
236
|
+
lines.push(`- ... and ${pending.length - 10} more`);
|
|
237
|
+
}
|
|
238
|
+
}
|
|
239
|
+
if (recent.length > 0) {
|
|
240
|
+
lines.push('', '### Recently Completed:');
|
|
241
|
+
for (const task of recent) {
|
|
242
|
+
const icon = task.status === 'completed' ? '✓' : '✗';
|
|
243
|
+
lines.push(`- [${icon}] ${task.content}`);
|
|
244
|
+
}
|
|
245
|
+
}
|
|
246
|
+
return lines.join('\n');
|
|
247
|
+
}
|
|
248
|
+
}
|
package/dist/index.d.ts
CHANGED
package/dist/index.js
CHANGED
package/dist/orchestrator.d.ts
CHANGED
|
@@ -5,6 +5,7 @@
|
|
|
5
5
|
* It does NOT do the work itself. Claude Code CLI workers do the work.
|
|
6
6
|
*/
|
|
7
7
|
import type { MessageParam } from '@anthropic-ai/sdk/resources/messages';
|
|
8
|
+
import { LiaTask, LiaPlan } from './core/task-queue.js';
|
|
8
9
|
export type MessageSender = (message: string) => Promise<void>;
|
|
9
10
|
export interface WorkerStatus {
|
|
10
11
|
id: string;
|
|
@@ -15,6 +16,7 @@ export interface WorkerStatus {
|
|
|
15
16
|
estimated: number;
|
|
16
17
|
}
|
|
17
18
|
export type WorkerStatusCallback = (workers: WorkerStatus[]) => void;
|
|
19
|
+
export type WorkerLogCallback = (workerId: string, line: string, stream: 'stdout' | 'stderr') => void;
|
|
18
20
|
export interface WorkerAsset {
|
|
19
21
|
path: string;
|
|
20
22
|
name: string;
|
|
@@ -33,17 +35,28 @@ export interface GalleryWorker {
|
|
|
33
35
|
assets: WorkerAsset[];
|
|
34
36
|
}
|
|
35
37
|
export type GalleryCallback = (workers: GalleryWorker[]) => void;
|
|
38
|
+
export type InstanceMode = 'personal' | 'team';
|
|
36
39
|
export declare class MasterOrchestrator {
|
|
37
40
|
private client;
|
|
38
41
|
private model;
|
|
39
42
|
private maxTokens;
|
|
40
43
|
private memory;
|
|
44
|
+
private contextGraph;
|
|
45
|
+
private orgMemory?;
|
|
46
|
+
private orgContextGraph?;
|
|
47
|
+
private orgId?;
|
|
48
|
+
private orgRole?;
|
|
49
|
+
private instanceMode;
|
|
50
|
+
private userOrgMemories;
|
|
51
|
+
private userOrgContextGraphs;
|
|
41
52
|
private scheduler;
|
|
42
53
|
private indexer;
|
|
43
54
|
private jobs;
|
|
44
55
|
private jobCounter;
|
|
45
56
|
private workerStatusCallback;
|
|
57
|
+
private workerLogCallback;
|
|
46
58
|
private workerStatusInterval;
|
|
59
|
+
private attachmentContext;
|
|
47
60
|
private galleryCallback;
|
|
48
61
|
private userId;
|
|
49
62
|
private workspaceDir;
|
|
@@ -52,15 +65,23 @@ export declare class MasterOrchestrator {
|
|
|
52
65
|
private bashTool;
|
|
53
66
|
private webTools;
|
|
54
67
|
private workerTools;
|
|
68
|
+
private calendarTools;
|
|
55
69
|
private sharedState;
|
|
56
70
|
private verboseMode;
|
|
71
|
+
private taskQueue;
|
|
72
|
+
private taskUpdateCallback?;
|
|
73
|
+
private planUpdateCallback?;
|
|
57
74
|
constructor(options: {
|
|
58
75
|
apiKey: string;
|
|
59
76
|
userId: string;
|
|
77
|
+
orgId?: string;
|
|
78
|
+
orgRole?: 'owner' | 'member';
|
|
60
79
|
workspaceDir?: string;
|
|
61
80
|
model?: string;
|
|
62
81
|
maxTokens?: number;
|
|
63
82
|
voyageApiKey?: string;
|
|
83
|
+
instanceMode?: InstanceMode;
|
|
84
|
+
userOrgIds?: string[];
|
|
64
85
|
});
|
|
65
86
|
/**
|
|
66
87
|
* Initialize the orchestrator - indexes filesystem on first call
|
|
@@ -70,10 +91,60 @@ export declare class MasterOrchestrator {
|
|
|
70
91
|
* Set callback for worker status updates (for UI progress bars)
|
|
71
92
|
*/
|
|
72
93
|
setWorkerStatusCallback(callback: WorkerStatusCallback | null): void;
|
|
94
|
+
/**
|
|
95
|
+
* Set callback for worker log streaming
|
|
96
|
+
*/
|
|
97
|
+
setWorkerLogCallback(callback: WorkerLogCallback | null): void;
|
|
73
98
|
/**
|
|
74
99
|
* Set callback for gallery updates (worker assets for UI gallery view)
|
|
75
100
|
*/
|
|
76
101
|
setGalleryCallback(callback: GalleryCallback | null): void;
|
|
102
|
+
/**
|
|
103
|
+
* Set callback for task queue updates (for real-time UI updates)
|
|
104
|
+
*/
|
|
105
|
+
setTaskUpdateCallback(callback: ((task: LiaTask) => void) | null): void;
|
|
106
|
+
/**
|
|
107
|
+
* Set callback for plan updates (for real-time UI updates)
|
|
108
|
+
*/
|
|
109
|
+
setPlanUpdateCallback(callback: ((plan: LiaPlan) => void) | null): void;
|
|
110
|
+
/**
|
|
111
|
+
* Queue a message for async processing (non-blocking)
|
|
112
|
+
* Returns immediately with task ID - use callbacks or getTaskQueueStatus for results
|
|
113
|
+
*/
|
|
114
|
+
queueMessage(message: string, options?: {
|
|
115
|
+
priority?: 'urgent' | 'high' | 'normal' | 'low';
|
|
116
|
+
apiKey?: string;
|
|
117
|
+
messageId?: string;
|
|
118
|
+
}): LiaTask;
|
|
119
|
+
/**
|
|
120
|
+
* Process a task from the queue (called by LiaTaskQueue)
|
|
121
|
+
*/
|
|
122
|
+
private processQueuedTask;
|
|
123
|
+
/**
|
|
124
|
+
* Get task queue status (for UI)
|
|
125
|
+
*/
|
|
126
|
+
getTaskQueueStatus(): {
|
|
127
|
+
isProcessing: boolean;
|
|
128
|
+
currentTask: LiaTask | null;
|
|
129
|
+
pendingCount: number;
|
|
130
|
+
queuePreview: string[];
|
|
131
|
+
};
|
|
132
|
+
/**
|
|
133
|
+
* Get pending tasks
|
|
134
|
+
*/
|
|
135
|
+
getPendingTasks(): LiaTask[];
|
|
136
|
+
/**
|
|
137
|
+
* Get task by ID
|
|
138
|
+
*/
|
|
139
|
+
getTask(taskId: string): LiaTask | null;
|
|
140
|
+
/**
|
|
141
|
+
* Cancel a pending task
|
|
142
|
+
*/
|
|
143
|
+
cancelTask(taskId: string): boolean;
|
|
144
|
+
/**
|
|
145
|
+
* Get Lia's internal todo list formatted for context
|
|
146
|
+
*/
|
|
147
|
+
getTaskQueueContext(): string;
|
|
77
148
|
/**
|
|
78
149
|
* Get gallery workers with their assets
|
|
79
150
|
*/
|
|
@@ -102,6 +173,8 @@ export declare class MasterOrchestrator {
|
|
|
102
173
|
* Handle slash commands
|
|
103
174
|
*/
|
|
104
175
|
private handleSlashCommand;
|
|
176
|
+
private extractAttachmentContext;
|
|
177
|
+
private withAttachments;
|
|
105
178
|
/**
|
|
106
179
|
* Check if verbose mode is enabled
|
|
107
180
|
*/
|
|
@@ -111,14 +184,31 @@ export declare class MasterOrchestrator {
|
|
|
111
184
|
* @param apiKey Optional per-request API key (overrides default)
|
|
112
185
|
*/
|
|
113
186
|
processMessage(message: string, conversationHistory?: MessageParam[], sendMessage?: MessageSender, apiKey?: string): Promise<string>;
|
|
187
|
+
private tryHandleCalendarTodo;
|
|
188
|
+
private extractTodoItems;
|
|
189
|
+
private extractCalendarEvents;
|
|
190
|
+
private parseCalendarEvent;
|
|
191
|
+
private parseExplicitDateTimeEvent;
|
|
192
|
+
private to24h;
|
|
193
|
+
private nextOccurrence;
|
|
194
|
+
private toDateKey;
|
|
114
195
|
/**
|
|
115
196
|
* Get relevant memory context for a message
|
|
197
|
+
* Routes based on instanceMode:
|
|
198
|
+
* - personal: search personal memories + all team memories
|
|
199
|
+
* - team: search ONLY team memory (never personal - team Lia is isolated)
|
|
116
200
|
*/
|
|
117
201
|
private getMemoryContext;
|
|
118
202
|
/**
|
|
119
203
|
* Auto-remember important information from conversations
|
|
204
|
+
* Routes based on instanceMode:
|
|
205
|
+
* - personal: write to personal memory
|
|
206
|
+
* - team: write to team memory
|
|
120
207
|
*/
|
|
121
208
|
private autoRemember;
|
|
209
|
+
private autoCaptureContext;
|
|
210
|
+
private normalizeEntityName;
|
|
211
|
+
private isIgnoredEntity;
|
|
122
212
|
/**
|
|
123
213
|
* Run the agentic loop
|
|
124
214
|
* @param apiKey Optional per-request API key (creates temporary client)
|
|
@@ -162,10 +252,16 @@ export declare class MasterOrchestrator {
|
|
|
162
252
|
private extractWorkerMemories;
|
|
163
253
|
/**
|
|
164
254
|
* Execute remember tool
|
|
255
|
+
* Routes writes based on instanceMode:
|
|
256
|
+
* - personal: write to personal memory only
|
|
257
|
+
* - team: write to team memory only
|
|
165
258
|
*/
|
|
166
259
|
private executeRemember;
|
|
167
260
|
/**
|
|
168
261
|
* Execute search memory tool
|
|
262
|
+
* Routes searches based on instanceMode:
|
|
263
|
+
* - personal: search personal + all team memories
|
|
264
|
+
* - team: search team memory only (never personal)
|
|
169
265
|
*/
|
|
170
266
|
private executeSearchMemory;
|
|
171
267
|
/**
|
|
@@ -192,6 +288,18 @@ export declare class MasterOrchestrator {
|
|
|
192
288
|
* Stop a running server on a port
|
|
193
289
|
*/
|
|
194
290
|
private executeStopServer;
|
|
291
|
+
/**
|
|
292
|
+
* Create a plan with steps (Lia's internal planning)
|
|
293
|
+
*/
|
|
294
|
+
private executeLiaPlan;
|
|
295
|
+
/**
|
|
296
|
+
* Add a task to Lia's internal queue
|
|
297
|
+
*/
|
|
298
|
+
private executeLiaAddTask;
|
|
299
|
+
/**
|
|
300
|
+
* Get Lia's task queue status
|
|
301
|
+
*/
|
|
302
|
+
private executeLiaGetQueue;
|
|
195
303
|
/**
|
|
196
304
|
* Format tool preview for user
|
|
197
305
|
*/
|