@siftd/connect-agent 0.2.53 → 0.2.55

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 CHANGED
@@ -415,8 +415,15 @@ export async function runAgent(pollInterval = 2000) {
415
415
  });
416
416
  // Worker results - send to user when workers complete
417
417
  orchestrator.setWorkerResultCallback((workerId, result) => {
418
- console.log(`[WORKER DONE] ${workerId}: ${result.slice(0, 100)}...`);
419
- if (wsClient?.connected()) {
418
+ const trimmed = result.trim();
419
+ const lower = trimmed.toLowerCase();
420
+ const shouldSuppress = !trimmed ||
421
+ trimmed === '(No output)' ||
422
+ lower.startsWith('worker timed out') ||
423
+ lower.startsWith('worker error') ||
424
+ lower.includes('partial output: none');
425
+ console.log(`[WORKER DONE] ${workerId}: ${trimmed.slice(0, 100)}${shouldSuppress ? ' (suppressed)' : ''}`);
426
+ if (!shouldSuppress && wsClient?.connected()) {
420
427
  // Send as response with worker ID as message ID
421
428
  wsClient.sendResponse(workerId, `**Worker completed:**\n\n${result}`);
422
429
  }
@@ -734,15 +734,18 @@ export class MasterOrchestrator {
734
734
  }
735
735
  hasCalendarMutation(message) {
736
736
  const lower = this.stripTodoSnapshot(message).toLowerCase();
737
- const target = /(^|\s)\/cal\b|\/calendar\b|\bcalendar\b|\bcal\b/.test(lower);
738
- const action = /\b(add|create|schedule|book|move|reschedule|update|change|cancel|delete|remove)\b/.test(lower);
737
+ const target = /(^|\s)\/cal\b|\/calendar\b|\bcalendar\b|\bcal\b|\bschedule\b|\bagenda\b/.test(lower);
738
+ const action = /\b(add|create|schedule|book|move|reschedule|update|change|cancel|delete|remove|set|put|place|remind)\b/.test(lower);
739
+ const dateCue = /\b(today|tomorrow|tonight|next|this\s+(week|month|year)|mon(day)?|tue(sday)?|wed(nesday)?|thu(rsday)?|fri(day)?|sat(urday)?|sun(day)?|\b\d{1,2}(:\d{2})?\s?(am|pm)\b|\b\d{1,2}\/\d{1,2}\b|\b\d{4}-\d{2}-\d{2}\b)\b/.test(lower);
740
+ const eventCue = /\b(meeting|call|appointment|event|reminder|session|class|lecture|review|deadline)\b/.test(lower);
741
+ const implicitTarget = eventCue && dateCue;
739
742
  const query = /\b(what|show|list|open|view|see)\b/.test(lower);
740
- return target && action && !query;
743
+ return (target || implicitTarget) && action && !query;
741
744
  }
742
745
  hasFileMutation(message) {
743
746
  const lower = this.stripTodoSnapshot(message).toLowerCase();
744
747
  const target = /(^|\s)\/files\b|\bfiles?\b/.test(lower);
745
- const action = /\b(create|make|write|save|generate|export|upload|attach|produce|edit|update|modify|delete|remove)\b/.test(lower);
748
+ const action = /\b(create|make|write|save|generate|export|upload|attach|produce|edit|update|modify|delete|remove|add|append|insert|include)\b/.test(lower);
746
749
  const query = /\b(what|show|list|open|view|see|browse|find)\b/.test(lower);
747
750
  return target && action && !query;
748
751
  }
@@ -1422,9 +1425,9 @@ ${hubContextStr}
1422
1425
  const toolsBase = this.getToolDefinitions();
1423
1426
  const last = messages[messages.length - 1];
1424
1427
  const lastContent = last && last.role === 'user' && typeof last.content === 'string' ? last.content : '';
1425
- const restrictWorkers = lastContent
1426
- ? (this.hasTodoMutation(lastContent) || this.hasCalendarMutation(lastContent))
1427
- : false;
1428
+ const requiredTodo = lastContent ? this.hasTodoMutation(lastContent) : false;
1429
+ const requiredCal = lastContent ? this.hasCalendarMutation(lastContent) : false;
1430
+ const restrictWorkers = requiredTodo || requiredCal;
1428
1431
  const wantsTodoOrCal = restrictWorkers;
1429
1432
  const todoCalTools = new Set(['calendar_upsert_events', 'todo_upsert_items']);
1430
1433
  const blockedTools = new Set([
@@ -1444,6 +1447,7 @@ ${hubContextStr}
1444
1447
  const requestTimeoutMs = 60000;
1445
1448
  const forcedToolChoice = this.getToolChoice(currentMessages);
1446
1449
  let retriedForcedTool = false;
1450
+ let retriedTodoCal = false;
1447
1451
  while (iterations < maxIterations) {
1448
1452
  iterations++;
1449
1453
  const toolChoice = forcedToolChoice ?? this.getToolChoice(currentMessages);
@@ -1492,13 +1496,44 @@ ${hubContextStr}
1492
1496
  ];
1493
1497
  continue;
1494
1498
  }
1499
+ if (wantsTodoOrCal && !retriedTodoCal && (requiredTodo || requiredCal)) {
1500
+ retriedTodoCal = true;
1501
+ const required = [
1502
+ requiredTodo ? 'todo_upsert_items' : null,
1503
+ requiredCal ? 'calendar_upsert_events' : null
1504
+ ].filter(Boolean).join(' and ');
1505
+ const followup = `You must call ${required} now. Do not spawn workers or call any other tools.`;
1506
+ currentMessages = [
1507
+ ...currentMessages,
1508
+ { role: 'assistant', content: response.content },
1509
+ { role: 'user', content: followup }
1510
+ ];
1511
+ continue;
1512
+ }
1495
1513
  return this.extractText(response.content);
1496
1514
  }
1497
1515
  const toolUseBlocks = response.content.filter((block) => block.type === 'tool_use');
1498
1516
  const toolNames = toolUseBlocks.map((block) => block.name);
1499
1517
  // Process tool calls
1500
1518
  const toolResults = await this.processToolCalls(response.content, sendMessage);
1501
- if (wantsTodoOrCal && (toolNames.includes('todo_upsert_items') || toolNames.includes('calendar_upsert_events'))) {
1519
+ const missingTodo = requiredTodo && !toolNames.includes('todo_upsert_items');
1520
+ const missingCal = requiredCal && !toolNames.includes('calendar_upsert_events');
1521
+ if (wantsTodoOrCal && (missingTodo || missingCal) && !retriedTodoCal) {
1522
+ retriedTodoCal = true;
1523
+ const required = [
1524
+ missingTodo ? 'todo_upsert_items' : null,
1525
+ missingCal ? 'calendar_upsert_events' : null
1526
+ ].filter(Boolean).join(' and ');
1527
+ const followup = `You must call ${required} now. Do not repeat tools already called. Do not spawn workers.`;
1528
+ currentMessages = [
1529
+ ...currentMessages,
1530
+ { role: 'assistant', content: response.content },
1531
+ { role: 'user', content: toolResults },
1532
+ { role: 'user', content: followup }
1533
+ ];
1534
+ continue;
1535
+ }
1536
+ if (wantsTodoOrCal && !missingTodo && !missingCal) {
1502
1537
  const updates = [];
1503
1538
  if (toolNames.includes('todo_upsert_items'))
1504
1539
  updates.push('Updated /todo.');
@@ -2209,6 +2244,11 @@ Unlike lia_plan (internal only), this creates a VISIBLE todo list that appears i
2209
2244
  try {
2210
2245
  const pathValue = String(input.path || '').trim();
2211
2246
  const content = String(input.content || '');
2247
+ const allowEmpty = /\b(empty|blank|placeholder)\b/i.test(this.lastUserMessage || '');
2248
+ if (!content.trim() && !allowEmpty) {
2249
+ result = { success: false, output: '', error: 'Refusing to write empty file content.' };
2250
+ break;
2251
+ }
2212
2252
  const targetPath = this.resolveFilesWritePath(pathValue);
2213
2253
  mkdirSync(dirname(targetPath), { recursive: true });
2214
2254
  writeFileSync(targetPath, content, 'utf8');
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@siftd/connect-agent",
3
- "version": "0.2.53",
3
+ "version": "0.2.55",
4
4
  "description": "Master orchestrator agent - control Claude Code remotely via web",
5
5
  "type": "module",
6
6
  "main": "dist/index.js",