@siftd/connect-agent 0.2.36 → 0.2.38
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/orchestrator.d.ts +46 -0
- package/dist/orchestrator.js +874 -26
- package/dist/tools/bash.js +4 -1
- 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,247 @@
|
|
|
1
|
+
const ENTITY_PREFIX = 'CTX_ENTITY ';
|
|
2
|
+
const RELATION_PREFIX = 'CTX_REL ';
|
|
3
|
+
const DEFAULT_SCOPE = 'user';
|
|
4
|
+
const ENTITY_KINDS = [
|
|
5
|
+
'person',
|
|
6
|
+
'team',
|
|
7
|
+
'project',
|
|
8
|
+
'org',
|
|
9
|
+
'role',
|
|
10
|
+
'tool',
|
|
11
|
+
'document',
|
|
12
|
+
'task',
|
|
13
|
+
'system',
|
|
14
|
+
'topic'
|
|
15
|
+
];
|
|
16
|
+
const RELATION_KINDS = [
|
|
17
|
+
'member_of',
|
|
18
|
+
'leads',
|
|
19
|
+
'works_on',
|
|
20
|
+
'owns',
|
|
21
|
+
'depends_on',
|
|
22
|
+
'blocked_by',
|
|
23
|
+
'related_to',
|
|
24
|
+
'uses',
|
|
25
|
+
'reports_to',
|
|
26
|
+
'supports',
|
|
27
|
+
'delivers_to',
|
|
28
|
+
'requested_by',
|
|
29
|
+
'scheduled_for'
|
|
30
|
+
];
|
|
31
|
+
function slugify(value) {
|
|
32
|
+
return value
|
|
33
|
+
.toLowerCase()
|
|
34
|
+
.replace(/[^a-z0-9]+/g, '-')
|
|
35
|
+
.replace(/(^-|-$)/g, '');
|
|
36
|
+
}
|
|
37
|
+
function buildEntityKey(kind, name) {
|
|
38
|
+
return `${kind}:${slugify(name)}`;
|
|
39
|
+
}
|
|
40
|
+
function normalizeTags(tags) {
|
|
41
|
+
return Array.from(new Set(tags.filter(Boolean)));
|
|
42
|
+
}
|
|
43
|
+
export class ContextGraph {
|
|
44
|
+
memory;
|
|
45
|
+
scope;
|
|
46
|
+
constructor(memory, options) {
|
|
47
|
+
this.memory = memory;
|
|
48
|
+
this.scope = options?.scope || DEFAULT_SCOPE;
|
|
49
|
+
}
|
|
50
|
+
async upsertEntity(input) {
|
|
51
|
+
const name = input.name.trim();
|
|
52
|
+
const kind = input.kind;
|
|
53
|
+
const key = input.key ? this.normalizeKey(input.key, kind, name) : buildEntityKey(kind, name);
|
|
54
|
+
const existing = await this.findEntityByKey(key);
|
|
55
|
+
if (existing) {
|
|
56
|
+
const parsed = parseEntity(existing.content);
|
|
57
|
+
if (parsed)
|
|
58
|
+
return parsed;
|
|
59
|
+
}
|
|
60
|
+
const payload = {
|
|
61
|
+
key,
|
|
62
|
+
kind,
|
|
63
|
+
name,
|
|
64
|
+
description: input.description?.trim() || undefined,
|
|
65
|
+
attributes: input.attributes,
|
|
66
|
+
source: input.source || 'context-graph',
|
|
67
|
+
updatedAt: new Date().toISOString()
|
|
68
|
+
};
|
|
69
|
+
const tags = normalizeTags([
|
|
70
|
+
'ctx:entity',
|
|
71
|
+
`ctx:kind:${kind}`,
|
|
72
|
+
`ctx:key:${key}`,
|
|
73
|
+
`ctx:name:${slugify(name)}`,
|
|
74
|
+
`ctx:scope:${this.scope}`,
|
|
75
|
+
...(input.tags || [])
|
|
76
|
+
]);
|
|
77
|
+
await this.memory.remember(`${ENTITY_PREFIX}${JSON.stringify(payload)}`, {
|
|
78
|
+
type: 'semantic',
|
|
79
|
+
source: payload.source,
|
|
80
|
+
importance: input.importance ?? 0.7,
|
|
81
|
+
tags
|
|
82
|
+
});
|
|
83
|
+
return payload;
|
|
84
|
+
}
|
|
85
|
+
async linkEntities(input) {
|
|
86
|
+
const fromEntity = await this.resolveEntity(input.from, input.fromKind);
|
|
87
|
+
const toEntity = await this.resolveEntity(input.to, input.toKind);
|
|
88
|
+
const relationKeyTags = [
|
|
89
|
+
'ctx:edge',
|
|
90
|
+
`ctx:rel:${input.type}`,
|
|
91
|
+
`ctx:from:${fromEntity.key}`,
|
|
92
|
+
`ctx:to:${toEntity.key}`,
|
|
93
|
+
`ctx:scope:${this.scope}`
|
|
94
|
+
];
|
|
95
|
+
const existing = await this.findByTags(relationKeyTags, 1);
|
|
96
|
+
if (existing.length > 0) {
|
|
97
|
+
const parsed = parseRelation(existing[0].content);
|
|
98
|
+
if (parsed)
|
|
99
|
+
return parsed;
|
|
100
|
+
}
|
|
101
|
+
const payload = {
|
|
102
|
+
from: fromEntity.key,
|
|
103
|
+
to: toEntity.key,
|
|
104
|
+
type: input.type,
|
|
105
|
+
description: input.description?.trim() || undefined,
|
|
106
|
+
source: input.source || 'context-graph',
|
|
107
|
+
updatedAt: new Date().toISOString()
|
|
108
|
+
};
|
|
109
|
+
await this.memory.remember(`${RELATION_PREFIX}${JSON.stringify(payload)}`, {
|
|
110
|
+
type: 'semantic',
|
|
111
|
+
source: payload.source,
|
|
112
|
+
importance: 0.6,
|
|
113
|
+
tags: relationKeyTags
|
|
114
|
+
});
|
|
115
|
+
return payload;
|
|
116
|
+
}
|
|
117
|
+
async searchEntities(query, options) {
|
|
118
|
+
const limit = options?.limit ?? 6;
|
|
119
|
+
const results = await this.memory.search(query, { limit: limit * 2 });
|
|
120
|
+
const entities = results
|
|
121
|
+
.map(result => parseEntity(result.content))
|
|
122
|
+
.filter((entity) => !!entity)
|
|
123
|
+
.filter(entity => !options?.kind || entity.kind === options.kind);
|
|
124
|
+
const deduped = new Map();
|
|
125
|
+
for (const entity of entities) {
|
|
126
|
+
if (!deduped.has(entity.key)) {
|
|
127
|
+
deduped.set(entity.key, entity);
|
|
128
|
+
}
|
|
129
|
+
}
|
|
130
|
+
return Array.from(deduped.values()).slice(0, limit);
|
|
131
|
+
}
|
|
132
|
+
async getRelationsForEntity(key, options) {
|
|
133
|
+
const limit = options?.limit ?? 6;
|
|
134
|
+
const outgoing = await this.findByTags(['ctx:edge', `ctx:from:${key}`], limit);
|
|
135
|
+
const incoming = await this.findByTags(['ctx:edge', `ctx:to:${key}`], limit);
|
|
136
|
+
const relations = [...outgoing, ...incoming]
|
|
137
|
+
.map(result => parseRelation(result.content))
|
|
138
|
+
.filter((rel) => !!rel);
|
|
139
|
+
const deduped = new Map();
|
|
140
|
+
for (const relation of relations) {
|
|
141
|
+
const relationKey = `${relation.from}|${relation.type}|${relation.to}`;
|
|
142
|
+
if (!deduped.has(relationKey)) {
|
|
143
|
+
deduped.set(relationKey, relation);
|
|
144
|
+
}
|
|
145
|
+
}
|
|
146
|
+
return Array.from(deduped.values()).slice(0, limit);
|
|
147
|
+
}
|
|
148
|
+
async getContextSummary(query, options) {
|
|
149
|
+
const entities = await this.searchEntities(query, { limit: options?.limit ?? 4 });
|
|
150
|
+
if (entities.length === 0)
|
|
151
|
+
return null;
|
|
152
|
+
const lines = [];
|
|
153
|
+
for (const entity of entities) {
|
|
154
|
+
const relations = await this.getRelationsForEntity(entity.key, { limit: 3 });
|
|
155
|
+
const relSummary = relations.map(relation => {
|
|
156
|
+
const other = relation.from === entity.key ? relation.to : relation.from;
|
|
157
|
+
return `${relation.type} -> ${other}`;
|
|
158
|
+
});
|
|
159
|
+
const desc = entity.description ? ` - ${entity.description}` : '';
|
|
160
|
+
const relText = relSummary.length > 0 ? ` (${relSummary.join('; ')})` : '';
|
|
161
|
+
lines.push(`[${entity.kind}] ${entity.name}${desc}${relText}`);
|
|
162
|
+
}
|
|
163
|
+
return lines.join('\n');
|
|
164
|
+
}
|
|
165
|
+
normalizeKey(value, kind, fallbackName) {
|
|
166
|
+
const trimmed = value.trim();
|
|
167
|
+
const match = trimmed.split(':');
|
|
168
|
+
if (match.length >= 2 && ENTITY_KINDS.includes(match[0])) {
|
|
169
|
+
return `${match[0]}:${slugify(match.slice(1).join(':'))}`;
|
|
170
|
+
}
|
|
171
|
+
if (trimmed.length > 0 && trimmed.includes(':')) {
|
|
172
|
+
return trimmed;
|
|
173
|
+
}
|
|
174
|
+
return buildEntityKey(kind, trimmed || fallbackName);
|
|
175
|
+
}
|
|
176
|
+
async resolveEntity(value, kind) {
|
|
177
|
+
const name = value.trim();
|
|
178
|
+
const inferredKind = kind || this.inferKind(name);
|
|
179
|
+
const key = this.normalizeKey(name, inferredKind, name);
|
|
180
|
+
const existing = await this.findEntityByKey(key);
|
|
181
|
+
if (existing) {
|
|
182
|
+
const parsed = parseEntity(existing.content);
|
|
183
|
+
if (parsed)
|
|
184
|
+
return parsed;
|
|
185
|
+
}
|
|
186
|
+
return this.upsertEntity({ kind: inferredKind, name });
|
|
187
|
+
}
|
|
188
|
+
inferKind(value) {
|
|
189
|
+
const lower = value.toLowerCase();
|
|
190
|
+
if (lower.includes('team'))
|
|
191
|
+
return 'team';
|
|
192
|
+
if (lower.includes('project'))
|
|
193
|
+
return 'project';
|
|
194
|
+
if (lower.includes('org') || lower.includes('company'))
|
|
195
|
+
return 'org';
|
|
196
|
+
return 'topic';
|
|
197
|
+
}
|
|
198
|
+
async findEntityByKey(key) {
|
|
199
|
+
const results = await this.findByTags(['ctx:entity', `ctx:key:${key}`], 2);
|
|
200
|
+
if (results.length === 0)
|
|
201
|
+
return null;
|
|
202
|
+
return this.pickMostRecent(results);
|
|
203
|
+
}
|
|
204
|
+
async findByTags(tags, limit) {
|
|
205
|
+
if (this.memory.findByTags) {
|
|
206
|
+
return this.memory.findByTags(tags, { limit });
|
|
207
|
+
}
|
|
208
|
+
const query = tags.join(' ');
|
|
209
|
+
const results = await this.memory.search(query, { limit: limit * 2 });
|
|
210
|
+
return results.filter(result => tags.every(tag => result.tags.includes(tag))).slice(0, limit);
|
|
211
|
+
}
|
|
212
|
+
pickMostRecent(records) {
|
|
213
|
+
return records.sort((a, b) => b.timestamp.localeCompare(a.timestamp))[0];
|
|
214
|
+
}
|
|
215
|
+
}
|
|
216
|
+
function parseEntity(content) {
|
|
217
|
+
if (!content.startsWith(ENTITY_PREFIX))
|
|
218
|
+
return null;
|
|
219
|
+
const raw = content.slice(ENTITY_PREFIX.length).trim();
|
|
220
|
+
try {
|
|
221
|
+
const data = JSON.parse(raw);
|
|
222
|
+
if (!data.key || !data.kind || !data.name)
|
|
223
|
+
return null;
|
|
224
|
+
return data;
|
|
225
|
+
}
|
|
226
|
+
catch {
|
|
227
|
+
return null;
|
|
228
|
+
}
|
|
229
|
+
}
|
|
230
|
+
function parseRelation(content) {
|
|
231
|
+
if (!content.startsWith(RELATION_PREFIX))
|
|
232
|
+
return null;
|
|
233
|
+
const raw = content.slice(RELATION_PREFIX.length).trim();
|
|
234
|
+
try {
|
|
235
|
+
const data = JSON.parse(raw);
|
|
236
|
+
if (!data.from || !data.to || !data.type)
|
|
237
|
+
return null;
|
|
238
|
+
return data;
|
|
239
|
+
}
|
|
240
|
+
catch {
|
|
241
|
+
return null;
|
|
242
|
+
}
|
|
243
|
+
}
|
|
244
|
+
export const ContextGraphKinds = {
|
|
245
|
+
entities: ENTITY_KINDS,
|
|
246
|
+
relations: RELATION_KINDS
|
|
247
|
+
};
|
|
@@ -23,7 +23,7 @@ const TEXT_EXTENSIONS = new Set([
|
|
|
23
23
|
// Directories to skip
|
|
24
24
|
const SKIP_DIRS = new Set([
|
|
25
25
|
'node_modules', '.git', '.next', 'dist', 'build', '__pycache__',
|
|
26
|
-
'.cache', 'coverage', '.nyc_output', 'vendor', 'target'
|
|
26
|
+
'.cache', 'coverage', '.nyc_output', 'vendor', 'target', '.lia'
|
|
27
27
|
]);
|
|
28
28
|
// Max files to track (performance limit)
|
|
29
29
|
const MAX_FILES = 500;
|
|
@@ -165,6 +165,8 @@ function getFileType(ext) {
|
|
|
165
165
|
* Check if a file is worth tracking
|
|
166
166
|
*/
|
|
167
167
|
function isInterestingFile(name, ext) {
|
|
168
|
+
if (name.includes('.asset.json') || name.includes('.preview.json'))
|
|
169
|
+
return false;
|
|
168
170
|
// Skip hidden files (except some config files)
|
|
169
171
|
if (name.startsWith('.') && !TEXT_EXTENSIONS.has(ext))
|
|
170
172
|
return false;
|
package/dist/core/hub.js
CHANGED
|
@@ -4,7 +4,8 @@
|
|
|
4
4
|
* Manages structured memory files in ~/.connect-hub/
|
|
5
5
|
* Human-readable markdown files that the orchestrator reads/writes.
|
|
6
6
|
*/
|
|
7
|
-
import { existsSync, readFileSync, writeFileSync, appendFileSync, mkdirSync } from 'fs';
|
|
7
|
+
import { existsSync, readFileSync, writeFileSync, appendFileSync, mkdirSync, chmodSync, chownSync } from 'fs';
|
|
8
|
+
import { execSync } from 'child_process';
|
|
8
9
|
import { join } from 'path';
|
|
9
10
|
import { homedir } from 'os';
|
|
10
11
|
const HUB_DIR = join(homedir(), '.connect-hub');
|
|
@@ -15,6 +16,38 @@ const NOTEBOOK_A_DIR = join(LIA_HUB_DIR, 'notebook-a');
|
|
|
15
16
|
const DRAFTS_DIR = join(NOTEBOOK_A_DIR, 'drafts');
|
|
16
17
|
const SHARED_DIR = join(LIA_HUB_DIR, 'shared');
|
|
17
18
|
const OUTPUTS_DIR = join(SHARED_DIR, 'outputs');
|
|
19
|
+
const LIA_STATE_DIR = join(OUTPUTS_DIR, '.lia');
|
|
20
|
+
let cachedLiaGid = null;
|
|
21
|
+
function getLiaGid() {
|
|
22
|
+
if (cachedLiaGid !== null)
|
|
23
|
+
return cachedLiaGid;
|
|
24
|
+
try {
|
|
25
|
+
const gidText = execSync('id -g lia', { stdio: ['ignore', 'pipe', 'ignore'] })
|
|
26
|
+
.toString()
|
|
27
|
+
.trim();
|
|
28
|
+
const gid = Number(gidText);
|
|
29
|
+
cachedLiaGid = Number.isFinite(gid) ? gid : null;
|
|
30
|
+
}
|
|
31
|
+
catch {
|
|
32
|
+
cachedLiaGid = null;
|
|
33
|
+
}
|
|
34
|
+
return cachedLiaGid;
|
|
35
|
+
}
|
|
36
|
+
function ensureDirPermissions(dir) {
|
|
37
|
+
if (process.getuid?.() !== 0)
|
|
38
|
+
return;
|
|
39
|
+
const liaGid = getLiaGid();
|
|
40
|
+
if (liaGid === null)
|
|
41
|
+
return;
|
|
42
|
+
try {
|
|
43
|
+
chownSync(dir, 0, liaGid);
|
|
44
|
+
chmodSync(dir, 0o775);
|
|
45
|
+
}
|
|
46
|
+
catch (error) {
|
|
47
|
+
const message = error instanceof Error ? error.message : String(error);
|
|
48
|
+
console.warn(`[HUB] Permission fix failed for ${dir}: ${message}`);
|
|
49
|
+
}
|
|
50
|
+
}
|
|
18
51
|
/**
|
|
19
52
|
* Ensure Lia-Hub directory structure exists
|
|
20
53
|
* Called on agent start to create the orchestrator's workspace
|
|
@@ -26,6 +59,7 @@ export function ensureLiaHub() {
|
|
|
26
59
|
DRAFTS_DIR,
|
|
27
60
|
SHARED_DIR,
|
|
28
61
|
OUTPUTS_DIR,
|
|
62
|
+
LIA_STATE_DIR,
|
|
29
63
|
join(LIA_HUB_DIR, 'projects')
|
|
30
64
|
];
|
|
31
65
|
for (const dir of dirs) {
|
|
@@ -33,6 +67,7 @@ export function ensureLiaHub() {
|
|
|
33
67
|
mkdirSync(dir, { recursive: true });
|
|
34
68
|
console.log(`[HUB] Created: ${dir}`);
|
|
35
69
|
}
|
|
70
|
+
ensureDirPermissions(dir);
|
|
36
71
|
}
|
|
37
72
|
// Create template files if they don't exist
|
|
38
73
|
const agentsFile = join(LIA_HUB_DIR, 'AGENTS.md');
|
|
@@ -53,6 +53,11 @@ export declare class AdvancedMemoryStore {
|
|
|
53
53
|
includeAssociations?: boolean;
|
|
54
54
|
}): Promise<Memory[]>;
|
|
55
55
|
private textSearch;
|
|
56
|
+
findByTags(tags: string[], options?: {
|
|
57
|
+
limit?: number;
|
|
58
|
+
type?: MemoryType;
|
|
59
|
+
minImportance?: number;
|
|
60
|
+
}): Promise<Memory[]>;
|
|
56
61
|
getById(id: string): Memory | null;
|
|
57
62
|
getByType(type: MemoryType, limit?: number): Memory[];
|
|
58
63
|
recall(id: string): Memory | null;
|
|
@@ -242,6 +242,33 @@ export class AdvancedMemoryStore {
|
|
|
242
242
|
const rows = stmt.all(...params, limit);
|
|
243
243
|
return rows.map(row => this.rowToMemory(row));
|
|
244
244
|
}
|
|
245
|
+
async findByTags(tags, options = {}) {
|
|
246
|
+
if (tags.length === 0)
|
|
247
|
+
return [];
|
|
248
|
+
const limit = options.limit || 20;
|
|
249
|
+
const conditions = ['1=1'];
|
|
250
|
+
const params = [];
|
|
251
|
+
if (options.type) {
|
|
252
|
+
conditions.push('type = ?');
|
|
253
|
+
params.push(options.type);
|
|
254
|
+
}
|
|
255
|
+
if (options.minImportance) {
|
|
256
|
+
conditions.push('importance >= ?');
|
|
257
|
+
params.push(options.minImportance);
|
|
258
|
+
}
|
|
259
|
+
for (const tag of tags) {
|
|
260
|
+
conditions.push('tags LIKE ?');
|
|
261
|
+
params.push(`%"${tag}"%`);
|
|
262
|
+
}
|
|
263
|
+
const stmt = this.db.prepare(`
|
|
264
|
+
SELECT * FROM memories
|
|
265
|
+
WHERE ${conditions.join(' AND ')}
|
|
266
|
+
ORDER BY lastAccessed DESC
|
|
267
|
+
LIMIT ?
|
|
268
|
+
`);
|
|
269
|
+
const rows = stmt.all(...params, limit);
|
|
270
|
+
return rows.map(row => this.rowToMemory(row));
|
|
271
|
+
}
|
|
245
272
|
getById(id) {
|
|
246
273
|
const stmt = this.db.prepare('SELECT * FROM memories WHERE id = ?');
|
|
247
274
|
const row = stmt.get(id);
|
|
@@ -58,10 +58,19 @@ export declare class PostgresMemoryStore {
|
|
|
58
58
|
limit?: number;
|
|
59
59
|
minImportance?: number;
|
|
60
60
|
}): Promise<Memory[]>;
|
|
61
|
+
/**
|
|
62
|
+
* Find memories that contain all provided tags
|
|
63
|
+
*/
|
|
64
|
+
findByTags(tags: string[], options?: {
|
|
65
|
+
type?: MemoryType;
|
|
66
|
+
limit?: number;
|
|
67
|
+
minImportance?: number;
|
|
68
|
+
}): Promise<Memory[]>;
|
|
61
69
|
/**
|
|
62
70
|
* Get memory by ID
|
|
63
71
|
*/
|
|
64
72
|
get(id: string): Promise<Memory | null>;
|
|
73
|
+
getById(id: string): Promise<Memory | null>;
|
|
65
74
|
/**
|
|
66
75
|
* Delete a memory
|
|
67
76
|
*/
|
|
@@ -197,6 +197,51 @@ export class PostgresMemoryStore {
|
|
|
197
197
|
hasEmbedding: true
|
|
198
198
|
}));
|
|
199
199
|
}
|
|
200
|
+
/**
|
|
201
|
+
* Find memories that contain all provided tags
|
|
202
|
+
*/
|
|
203
|
+
async findByTags(tags, options) {
|
|
204
|
+
await this.initialize();
|
|
205
|
+
if (tags.length === 0)
|
|
206
|
+
return [];
|
|
207
|
+
const limit = options?.limit || 20;
|
|
208
|
+
const minImportance = options?.minImportance || 0;
|
|
209
|
+
const params = [
|
|
210
|
+
this.userId,
|
|
211
|
+
tags,
|
|
212
|
+
minImportance
|
|
213
|
+
];
|
|
214
|
+
let sql = `
|
|
215
|
+
SELECT id, type, content, summary, source, timestamp, last_accessed,
|
|
216
|
+
importance, access_count, decay_rate, associations, tags
|
|
217
|
+
FROM memories
|
|
218
|
+
WHERE user_id = $1
|
|
219
|
+
AND tags @> $2::text[]
|
|
220
|
+
AND importance >= $3
|
|
221
|
+
`;
|
|
222
|
+
if (options?.type) {
|
|
223
|
+
sql += ` AND type = $${params.length + 1}`;
|
|
224
|
+
params.push(options.type);
|
|
225
|
+
}
|
|
226
|
+
sql += ` ORDER BY last_accessed DESC LIMIT $${params.length + 1}`;
|
|
227
|
+
params.push(limit);
|
|
228
|
+
const result = await this.pool.query(sql, params);
|
|
229
|
+
return result.rows.map(row => ({
|
|
230
|
+
id: row.id,
|
|
231
|
+
type: row.type,
|
|
232
|
+
content: row.content,
|
|
233
|
+
summary: row.summary,
|
|
234
|
+
source: row.source,
|
|
235
|
+
timestamp: row.timestamp.toISOString(),
|
|
236
|
+
lastAccessed: row.last_accessed.toISOString(),
|
|
237
|
+
importance: row.importance,
|
|
238
|
+
accessCount: row.access_count,
|
|
239
|
+
decayRate: row.decay_rate,
|
|
240
|
+
associations: row.associations || [],
|
|
241
|
+
tags: row.tags || [],
|
|
242
|
+
hasEmbedding: true
|
|
243
|
+
}));
|
|
244
|
+
}
|
|
200
245
|
/**
|
|
201
246
|
* Get memory by ID
|
|
202
247
|
*/
|
|
@@ -224,6 +269,9 @@ export class PostgresMemoryStore {
|
|
|
224
269
|
hasEmbedding: true
|
|
225
270
|
};
|
|
226
271
|
}
|
|
272
|
+
async getById(id) {
|
|
273
|
+
return this.get(id);
|
|
274
|
+
}
|
|
227
275
|
/**
|
|
228
276
|
* Delete a memory
|
|
229
277
|
*/
|
|
@@ -23,6 +23,7 @@ import { getSharedOutputPath } from './hub.js';
|
|
|
23
23
|
const IGNORE_PATTERNS = [
|
|
24
24
|
'**/*.asset.json',
|
|
25
25
|
'**/*.preview.json',
|
|
26
|
+
'**/.lia/**',
|
|
26
27
|
'**/.thumbs/**',
|
|
27
28
|
'**/thumbs/**',
|
|
28
29
|
'**/*.tmp',
|
|
@@ -140,6 +141,8 @@ export class PreviewWorker {
|
|
|
140
141
|
// Skip if file no longer exists
|
|
141
142
|
if (!existsSync(filePath))
|
|
142
143
|
return;
|
|
144
|
+
if (filePath.includes('.asset.json') || filePath.includes('.preview.json'))
|
|
145
|
+
return;
|
|
143
146
|
const startTime = Date.now();
|
|
144
147
|
// Check for existing manifest
|
|
145
148
|
let manifest = readAssetManifest(filePath);
|
package/dist/orchestrator.d.ts
CHANGED
|
@@ -15,6 +15,7 @@ export interface WorkerStatus {
|
|
|
15
15
|
estimated: number;
|
|
16
16
|
}
|
|
17
17
|
export type WorkerStatusCallback = (workers: WorkerStatus[]) => void;
|
|
18
|
+
export type WorkerLogCallback = (workerId: string, line: string, stream: 'stdout' | 'stderr') => void;
|
|
18
19
|
export interface WorkerAsset {
|
|
19
20
|
path: string;
|
|
20
21
|
name: string;
|
|
@@ -33,17 +34,28 @@ export interface GalleryWorker {
|
|
|
33
34
|
assets: WorkerAsset[];
|
|
34
35
|
}
|
|
35
36
|
export type GalleryCallback = (workers: GalleryWorker[]) => void;
|
|
37
|
+
export type InstanceMode = 'personal' | 'team';
|
|
36
38
|
export declare class MasterOrchestrator {
|
|
37
39
|
private client;
|
|
38
40
|
private model;
|
|
39
41
|
private maxTokens;
|
|
40
42
|
private memory;
|
|
43
|
+
private contextGraph;
|
|
44
|
+
private orgMemory?;
|
|
45
|
+
private orgContextGraph?;
|
|
46
|
+
private orgId?;
|
|
47
|
+
private orgRole?;
|
|
48
|
+
private instanceMode;
|
|
49
|
+
private userOrgMemories;
|
|
50
|
+
private userOrgContextGraphs;
|
|
41
51
|
private scheduler;
|
|
42
52
|
private indexer;
|
|
43
53
|
private jobs;
|
|
44
54
|
private jobCounter;
|
|
45
55
|
private workerStatusCallback;
|
|
56
|
+
private workerLogCallback;
|
|
46
57
|
private workerStatusInterval;
|
|
58
|
+
private attachmentContext;
|
|
47
59
|
private galleryCallback;
|
|
48
60
|
private userId;
|
|
49
61
|
private workspaceDir;
|
|
@@ -52,15 +64,20 @@ export declare class MasterOrchestrator {
|
|
|
52
64
|
private bashTool;
|
|
53
65
|
private webTools;
|
|
54
66
|
private workerTools;
|
|
67
|
+
private calendarTools;
|
|
55
68
|
private sharedState;
|
|
56
69
|
private verboseMode;
|
|
57
70
|
constructor(options: {
|
|
58
71
|
apiKey: string;
|
|
59
72
|
userId: string;
|
|
73
|
+
orgId?: string;
|
|
74
|
+
orgRole?: 'owner' | 'member';
|
|
60
75
|
workspaceDir?: string;
|
|
61
76
|
model?: string;
|
|
62
77
|
maxTokens?: number;
|
|
63
78
|
voyageApiKey?: string;
|
|
79
|
+
instanceMode?: InstanceMode;
|
|
80
|
+
userOrgIds?: string[];
|
|
64
81
|
});
|
|
65
82
|
/**
|
|
66
83
|
* Initialize the orchestrator - indexes filesystem on first call
|
|
@@ -70,6 +87,10 @@ export declare class MasterOrchestrator {
|
|
|
70
87
|
* Set callback for worker status updates (for UI progress bars)
|
|
71
88
|
*/
|
|
72
89
|
setWorkerStatusCallback(callback: WorkerStatusCallback | null): void;
|
|
90
|
+
/**
|
|
91
|
+
* Set callback for worker log streaming
|
|
92
|
+
*/
|
|
93
|
+
setWorkerLogCallback(callback: WorkerLogCallback | null): void;
|
|
73
94
|
/**
|
|
74
95
|
* Set callback for gallery updates (worker assets for UI gallery view)
|
|
75
96
|
*/
|
|
@@ -102,6 +123,8 @@ export declare class MasterOrchestrator {
|
|
|
102
123
|
* Handle slash commands
|
|
103
124
|
*/
|
|
104
125
|
private handleSlashCommand;
|
|
126
|
+
private extractAttachmentContext;
|
|
127
|
+
private withAttachments;
|
|
105
128
|
/**
|
|
106
129
|
* Check if verbose mode is enabled
|
|
107
130
|
*/
|
|
@@ -111,14 +134,31 @@ export declare class MasterOrchestrator {
|
|
|
111
134
|
* @param apiKey Optional per-request API key (overrides default)
|
|
112
135
|
*/
|
|
113
136
|
processMessage(message: string, conversationHistory?: MessageParam[], sendMessage?: MessageSender, apiKey?: string): Promise<string>;
|
|
137
|
+
private tryHandleCalendarTodo;
|
|
138
|
+
private extractTodoItems;
|
|
139
|
+
private extractCalendarEvents;
|
|
140
|
+
private parseCalendarEvent;
|
|
141
|
+
private parseExplicitDateTimeEvent;
|
|
142
|
+
private to24h;
|
|
143
|
+
private nextOccurrence;
|
|
144
|
+
private toDateKey;
|
|
114
145
|
/**
|
|
115
146
|
* Get relevant memory context for a message
|
|
147
|
+
* Routes based on instanceMode:
|
|
148
|
+
* - personal: search personal memories + all team memories
|
|
149
|
+
* - team: search ONLY team memory (never personal - team Lia is isolated)
|
|
116
150
|
*/
|
|
117
151
|
private getMemoryContext;
|
|
118
152
|
/**
|
|
119
153
|
* Auto-remember important information from conversations
|
|
154
|
+
* Routes based on instanceMode:
|
|
155
|
+
* - personal: write to personal memory
|
|
156
|
+
* - team: write to team memory
|
|
120
157
|
*/
|
|
121
158
|
private autoRemember;
|
|
159
|
+
private autoCaptureContext;
|
|
160
|
+
private normalizeEntityName;
|
|
161
|
+
private isIgnoredEntity;
|
|
122
162
|
/**
|
|
123
163
|
* Run the agentic loop
|
|
124
164
|
* @param apiKey Optional per-request API key (creates temporary client)
|
|
@@ -162,10 +202,16 @@ export declare class MasterOrchestrator {
|
|
|
162
202
|
private extractWorkerMemories;
|
|
163
203
|
/**
|
|
164
204
|
* Execute remember tool
|
|
205
|
+
* Routes writes based on instanceMode:
|
|
206
|
+
* - personal: write to personal memory only
|
|
207
|
+
* - team: write to team memory only
|
|
165
208
|
*/
|
|
166
209
|
private executeRemember;
|
|
167
210
|
/**
|
|
168
211
|
* Execute search memory tool
|
|
212
|
+
* Routes searches based on instanceMode:
|
|
213
|
+
* - personal: search personal + all team memories
|
|
214
|
+
* - team: search team memory only (never personal)
|
|
169
215
|
*/
|
|
170
216
|
private executeSearchMemory;
|
|
171
217
|
/**
|