@siftd/connect-agent 0.2.57 → 0.2.59

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.
@@ -76,6 +76,7 @@ export declare class MasterOrchestrator {
76
76
  private currentFileScope;
77
77
  private forceTeamWorkingDir;
78
78
  private recentFileWrites;
79
+ private recentWorkerActivity;
79
80
  private bashTool;
80
81
  private webTools;
81
82
  private workerTools;
@@ -214,6 +215,11 @@ export declare class MasterOrchestrator {
214
215
  private toFilesVirtualPath;
215
216
  private finalizeResponse;
216
217
  private getFileScopeSystemNote;
218
+ private recordWorkerActivity;
219
+ private formatRelativeTime;
220
+ private getRecentWorkerSummary;
221
+ private isStatusInquiry;
222
+ private buildStatusReply;
217
223
  /**
218
224
  * Check if verbose mode is enabled
219
225
  */
@@ -161,6 +161,13 @@ YOUR HUB: ~/Lia-Hub/
161
161
  - **notebook-a/SCRATCHPAD.md** - Your working notes
162
162
  - **shared/outputs/** - Where workers save files
163
163
 
164
+ HUB PRIVACY:
165
+ - These hub files are INTERNAL. Do NOT list or reveal them to users.
166
+ - Never enumerate ~/Lia-Hub, CLAUDE.md, AGENTS.md, LANDMARKS.md, or other internal files.
167
+ - If a user asks to list files, direct them to /files or list channel files only.
168
+ - Home channels are private. Never access or list another user's Home channel.
169
+ - If asked about another user's Home channel, politely decline and offer to search shared channels.
170
+
164
171
  ASSET PREVIEWS:
165
172
  The Lia interface has a built-in gallery that shows worker outputs automatically.
166
173
  ⛔ NEVER use start_local_server or open_browser for previews
@@ -170,6 +177,7 @@ The Lia interface has a built-in gallery that shows worker outputs automatically
170
177
 
171
178
  FILES BROWSER:
172
179
  Users can type /files to open the Finder UI (cloud mode).
180
+ Treat /files as the canonical file surface (channel files). Do not expose hub internals.
173
181
  When asked to browse or locate files, point them to /files.
174
182
  Refer to /files instead of internal Lia-Hub paths in responses.
175
183
  When users ask you to create or update a file in /files, use files_write.
@@ -259,6 +267,7 @@ export class MasterOrchestrator {
259
267
  currentFileScope = 'personal';
260
268
  forceTeamWorkingDir = false;
261
269
  recentFileWrites = [];
270
+ recentWorkerActivity = [];
262
271
  // New tools from whatsapp-claude
263
272
  bashTool;
264
273
  webTools;
@@ -992,6 +1001,98 @@ export class MasterOrchestrator {
992
1001
  return null;
993
1002
  return `TEAM FILES DIRECTORY:\n- ${teamDir}\nUse this path for files meant to be shared with the team.`;
994
1003
  }
1004
+ recordWorkerActivity(event) {
1005
+ const normalized = {
1006
+ ...event,
1007
+ task: event.task.slice(0, 140),
1008
+ note: event.note?.slice(0, 200)
1009
+ };
1010
+ this.recentWorkerActivity = [normalized, ...this.recentWorkerActivity].slice(0, 12);
1011
+ }
1012
+ formatRelativeTime(timestamp) {
1013
+ const deltaMs = Date.now() - timestamp;
1014
+ if (deltaMs < 1000)
1015
+ return 'just now';
1016
+ const seconds = Math.floor(deltaMs / 1000);
1017
+ if (seconds < 60)
1018
+ return `${seconds}s ago`;
1019
+ const minutes = Math.floor(seconds / 60);
1020
+ if (minutes < 60)
1021
+ return `${minutes}m ago`;
1022
+ const hours = Math.floor(minutes / 60);
1023
+ if (hours < 24)
1024
+ return `${hours}h ago`;
1025
+ const days = Math.floor(hours / 24);
1026
+ return `${days}d ago`;
1027
+ }
1028
+ getRecentWorkerSummary(limit = 4) {
1029
+ if (this.recentWorkerActivity.length === 0)
1030
+ return null;
1031
+ const seen = new Set();
1032
+ const lines = [];
1033
+ for (const event of this.recentWorkerActivity) {
1034
+ if (seen.has(event.id))
1035
+ continue;
1036
+ seen.add(event.id);
1037
+ const when = this.formatRelativeTime(event.endedAt ?? event.startedAt);
1038
+ const note = event.note ? ` — ${event.note}` : '';
1039
+ lines.push(`- ${event.task} (${event.status}, ${when})${note}`);
1040
+ if (lines.length >= limit)
1041
+ break;
1042
+ }
1043
+ return lines.length > 0 ? lines.join('\n') : null;
1044
+ }
1045
+ isStatusInquiry(message) {
1046
+ const lower = message.toLowerCase();
1047
+ if (!lower)
1048
+ return false;
1049
+ const patterns = [
1050
+ /\b(status|progress|queue|queued|pending)\b/,
1051
+ /\bwhat (are|were) you (doing|working on)\b/,
1052
+ /\bwhat happened\b/,
1053
+ /\bstuck\b/,
1054
+ /\btaking (so long|forever|too long)\b/,
1055
+ /\bwhy (is|are) (this|it) (taking|hung|stuck)\b/,
1056
+ ];
1057
+ return patterns.some((pattern) => pattern.test(lower));
1058
+ }
1059
+ buildStatusReply() {
1060
+ const queueStatus = this.taskQueue.getStatus();
1061
+ const runningWorkers = this.getWorkerStatus().filter((worker) => worker.status === 'running');
1062
+ const recentWorkers = this.getRecentWorkerSummary(4);
1063
+ const recentTasks = this.taskQueue.getRecentTasks(3)
1064
+ .filter((task) => task.status !== 'pending')
1065
+ .map((task) => `- ${task.content.slice(0, 80)} (${task.status})`);
1066
+ const lines = [];
1067
+ if (runningWorkers.length > 0) {
1068
+ lines.push(`Active workers: ${runningWorkers.length}`);
1069
+ for (const worker of runningWorkers.slice(0, 3)) {
1070
+ lines.push(`- ${worker.task} (${worker.progress}% · ${worker.elapsed}s)`);
1071
+ }
1072
+ }
1073
+ else {
1074
+ lines.push('Active workers: none');
1075
+ }
1076
+ if (queueStatus.isProcessing || queueStatus.pendingCount > 0) {
1077
+ const current = queueStatus.currentTask?.content;
1078
+ lines.push(`Task queue: ${queueStatus.pendingCount} pending${queueStatus.isProcessing ? ' · processing' : ''}`);
1079
+ if (current) {
1080
+ lines.push(`- Current: ${current.slice(0, 100)}`);
1081
+ }
1082
+ }
1083
+ else {
1084
+ lines.push('Task queue: empty');
1085
+ }
1086
+ if (recentWorkers) {
1087
+ lines.push('Recent worker activity:');
1088
+ lines.push(recentWorkers);
1089
+ }
1090
+ if (recentTasks.length > 0) {
1091
+ lines.push('Recent task results:');
1092
+ lines.push(recentTasks.join('\n'));
1093
+ }
1094
+ return lines.join('\n');
1095
+ }
995
1096
  /**
996
1097
  * Check if verbose mode is enabled
997
1098
  */
@@ -1010,6 +1111,9 @@ export class MasterOrchestrator {
1010
1111
  return slashResponse;
1011
1112
  }
1012
1113
  this.updateFileScope(cleanMessage);
1114
+ if (this.isStatusInquiry(cleanMessage)) {
1115
+ return this.buildStatusReply();
1116
+ }
1013
1117
  const wantsTodoOrCal = this.hasTodoMutation(cleanMessage) || this.hasCalendarMutation(cleanMessage);
1014
1118
  if (wantsTodoOrCal) {
1015
1119
  this.attachmentContext = this.extractAttachmentContext(message);
@@ -1066,6 +1170,10 @@ ${hubContextStr}
1066
1170
  if (fileScopeNote) {
1067
1171
  systemWithContext += `\n\n${fileScopeNote}`;
1068
1172
  }
1173
+ const recentWorkerSummary = this.getRecentWorkerSummary();
1174
+ if (recentWorkerSummary) {
1175
+ systemWithContext += `\n\nRECENT WORKER ACTIVITY (for accurate status updates):\n${recentWorkerSummary}`;
1176
+ }
1069
1177
  // Add user message
1070
1178
  const messages = [
1071
1179
  ...conversationHistory,
@@ -2336,6 +2444,17 @@ Unlike lia_plan (internal only), this creates a VISIBLE todo list that appears i
2336
2444
  priority: input.priority,
2337
2445
  workingDirectory: input.working_directory || fileScope.workingDir
2338
2446
  });
2447
+ if (result.success && result.output) {
2448
+ const match = result.output.match(/Job ID:\s*(\S+)/i);
2449
+ if (match) {
2450
+ this.recordWorkerActivity({
2451
+ id: match[1],
2452
+ task: normalizedTask.slice(0, 200),
2453
+ status: 'running',
2454
+ startedAt: Date.now(),
2455
+ });
2456
+ }
2457
+ }
2339
2458
  break;
2340
2459
  }
2341
2460
  case 'check_worker':
@@ -2349,6 +2468,16 @@ Unlike lia_plan (internal only), this creates a VISIBLE todo list that appears i
2349
2468
  break;
2350
2469
  case 'cancel_worker':
2351
2470
  result = await this.workerTools.cancelWorker(input.job_id);
2471
+ if (result.success) {
2472
+ this.recordWorkerActivity({
2473
+ id: String(input.job_id || 'unknown'),
2474
+ task: 'Worker cancelled',
2475
+ status: 'cancelled',
2476
+ startedAt: Date.now(),
2477
+ endedAt: Date.now(),
2478
+ note: 'Cancelled by request'
2479
+ });
2480
+ }
2352
2481
  break;
2353
2482
  // Legacy delegate tool
2354
2483
  case 'delegate_to_worker': {
@@ -2509,6 +2638,12 @@ Unlike lia_plan (internal only), this creates a VISIBLE todo list that appears i
2509
2638
  estimatedTime,
2510
2639
  workingDir: cwd
2511
2640
  };
2641
+ this.recordWorkerActivity({
2642
+ id,
2643
+ task: job.task,
2644
+ status: 'running',
2645
+ startedAt: job.startTime
2646
+ });
2512
2647
  // Escape single quotes in prompt for shell safety
2513
2648
  const escapedPrompt = prompt.replace(/'/g, "'\\''");
2514
2649
  const budget = resolveClaudeBudgetUsd();
@@ -2549,6 +2684,14 @@ Unlike lia_plan (internal only), this creates a VISIBLE todo list that appears i
2549
2684
  job.endTime = Date.now();
2550
2685
  child.kill('SIGTERM');
2551
2686
  console.log(`[ORCHESTRATOR] Worker ${id} timed out`);
2687
+ this.recordWorkerActivity({
2688
+ id,
2689
+ task: job.task,
2690
+ status: 'timeout',
2691
+ startedAt: job.startTime,
2692
+ endedAt: job.endTime,
2693
+ note: 'Timed out after 5 minutes'
2694
+ });
2552
2695
  if (this.workerResultCallback) {
2553
2696
  this.workerResultCallback(id, `Worker timed out. Partial output: ${job.output.slice(-1000) || 'none'}`);
2554
2697
  }
@@ -2586,6 +2729,14 @@ Unlike lia_plan (internal only), this creates a VISIBLE todo list that appears i
2586
2729
  const status = code === 0 ? 'completed' : 'failed';
2587
2730
  const filesCreated = assets.map(a => a.name);
2588
2731
  logWorker(id, job.task, status, duration, filesCreated.length > 0 ? filesCreated : undefined);
2732
+ this.recordWorkerActivity({
2733
+ id,
2734
+ task: job.task,
2735
+ status,
2736
+ startedAt: job.startTime,
2737
+ endedAt: job.endTime,
2738
+ note: code === 0 ? undefined : `Exit code ${code ?? 'unknown'}`
2739
+ });
2589
2740
  if (assets.length > 0) {
2590
2741
  console.log(`[ORCHESTRATOR] Worker ${id} created ${assets.length} files: ${assets.map(a => a.name).join(', ')}`);
2591
2742
  // Store in memory for gallery queries
@@ -2613,6 +2764,14 @@ Unlike lia_plan (internal only), this creates a VISIBLE todo list that appears i
2613
2764
  job.status = 'failed';
2614
2765
  job.endTime = Date.now();
2615
2766
  console.error(`[ORCHESTRATOR] Worker ${id} error:`, err.message);
2767
+ this.recordWorkerActivity({
2768
+ id,
2769
+ task: job.task,
2770
+ status: 'failed',
2771
+ startedAt: job.startTime,
2772
+ endedAt: job.endTime,
2773
+ note: err.message
2774
+ });
2616
2775
  if (this.workerResultCallback) {
2617
2776
  this.workerResultCallback(id, `Worker error: ${err.message}`);
2618
2777
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@siftd/connect-agent",
3
- "version": "0.2.57",
3
+ "version": "0.2.59",
4
4
  "description": "Master orchestrator agent - control Claude Code remotely via web",
5
5
  "type": "module",
6
6
  "main": "dist/index.js",