@gholl-studio/pier-connector 0.2.2 → 0.2.4

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/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "@gholl-studio/pier-connector",
3
3
  "author": "gholl",
4
- "version": "0.2.2",
4
+ "version": "0.2.4",
5
5
  "description": "OpenClaw plugin that connects to the Pier job marketplace. Automatically fetches, executes, and reports distributed tasks for rewards.",
6
6
  "type": "module",
7
7
  "main": "src/index.js",
package/src/index.js CHANGED
@@ -598,8 +598,8 @@ export default function register(api) {
598
598
  durable_name: durableName,
599
599
  filter_subject: msgSubject,
600
600
  deliver_policy: DeliverPolicy.New, // Reverted to New to stop message storms (BUG-27)
601
- ack_policy: AckPolicy.Explicit,
602
- ack_wait: 30000,
601
+ ack_policy: AckPolicy.Explicit, // Explicit ACK is crucial for deduplication
602
+ ack_wait: 1000 * 60 * 60, // 1 hour ACK wait to allow for long processing (BUG-30)
603
603
  });
604
604
  const consumer = await js.consumers.get(streamName, cInfo.name);
605
605
 
@@ -609,16 +609,23 @@ export default function register(api) {
609
609
  (async () => {
610
610
  logger.info(`[pier-connector] 👂 Monitoring chat subject ${msgSubject} (Durable: ${durableName})`);
611
611
 
612
- let isProcessing = false;
612
+ const processedMessages = new Set();
613
+
613
614
  for await (const msg of iter) {
614
615
  try {
615
616
  const rawMsg = new TextDecoder().decode(msg.data);
616
617
  let msgPayload;
617
618
  try {
618
619
  msgPayload = JSON.parse(rawMsg);
619
- } catch (e) {
620
+ } catch (e) {
621
+ msg.ack();
622
+ continue;
623
+ }
624
+
625
+ // Deduplication (prevents loops if NATS redelivers)
626
+ if (msgPayload.id && processedMessages.has(msgPayload.id)) {
620
627
  msg.ack();
621
- continue;
628
+ continue;
622
629
  }
623
630
 
624
631
  // Ignore my own messages
@@ -641,9 +648,8 @@ export default function register(api) {
641
648
  msg_id: msgPayload.id,
642
649
  reader_id: config.nodeId
643
650
  })));
644
- logger.debug(`[pier-connector] 👁️ Sent read receipt for message ${msgPayload.id}`);
645
651
  } catch (err) {
646
- logger.error(`[pier-connector] Failed to send read receipt: ${err.message}`);
652
+ // Silently ignore receipt failures
647
653
  }
648
654
  }
649
655
 
@@ -651,40 +657,37 @@ export default function register(api) {
651
657
  const senderCore = msgPayload.sender_id;
652
658
 
653
659
  if (!content) {
654
- logger.warn(`[pier-connector] ⚠️ Skipping message from ${senderCore} for ${jobId}: Empty content`);
655
660
  msg.ack();
656
661
  continue;
657
662
  }
658
663
 
659
- if (isProcessing) {
660
- logger.warn(`[pier-connector] ⏳ Throttling: Agent already processing for job ${jobId}`);
661
- msg.nak();
662
- continue;
663
- }
664
-
665
664
  logger.info(`[pier-connector] 📥 Incoming chat: [${jobId}] sender=${senderCore} "${truncate(content, 40)}"`);
666
665
 
666
+ if (msgPayload.id) processedMessages.add(msgPayload.id);
667
+
668
+ // ACK IMMEDIATELY to prevent NATS 30s timeout (BUG-27/30)
669
+ msg.ack();
670
+
667
671
  activeNodeJobs.set(jobId, {
668
672
  pierJobId: jobId,
669
673
  isRealtimeMsg: true
670
674
  });
671
675
 
672
- // Trigger agent
673
- try {
674
- isProcessing = true;
675
- await receiveIncoming({
676
- accountId: config.accountId || 'default',
677
- senderId: `pier:${senderCore}`,
678
- text: content,
679
- }, jobId);
680
- } finally {
681
- isProcessing = false;
682
- }
683
-
684
- msg.ack();
676
+ // Trigger agent (Asynchronously to NOT block the loop)
677
+ (async () => {
678
+ try {
679
+ logger.info(`[pier-connector] 🧠 Triggering agent for job ${jobId}...`);
680
+ await receiveIncoming({
681
+ accountId: config.accountId || 'default',
682
+ senderId: `pier:${senderCore}`,
683
+ text: content,
684
+ }, jobId);
685
+ } catch (err) {
686
+ logger.error(`[pier-connector] Agent execution error: ${err.message}`);
687
+ }
688
+ })();
685
689
  } catch (err) {
686
690
  logger.error(`[pier-connector] Chat message processing error for ${jobId}: ${err.message}`);
687
- msg.nak();
688
691
  }
689
692
  }
690
693
  })().catch(err => {
@@ -710,14 +713,21 @@ export default function register(api) {
710
713
  return;
711
714
  }
712
715
 
713
- // WAKEUP SIGNAL HANDLING
716
+ // WAKEUP SIGNAL HANDLING (BUG-26/29)
714
717
  if (payload.type === 'wakeup') {
715
718
  const { jobId } = payload;
716
719
  if (jobId) {
717
720
  logger.info(`[pier-connector] ⏰ Received wakeup signal for job ${jobId}`);
721
+ if (jobSubscriptions.has(jobId)) {
722
+ logger.info(`[pier-connector] 👂 Already monitoring job ${jobId}, skipping duplicate subscription.`);
723
+ } else {
724
+ // Important: Map the job ID so we know where to reply
725
+ activeNodeJobs.set(jobId, { pierJobId: jobId });
726
+ await subscribeToJobMessages(jobId);
727
+ }
718
728
  msg.ack();
719
- subscribeToJobMessages(jobId);
720
729
  } else {
730
+ logger.warn('[pier-connector] ⚠️ Wakeup signal received but jobId is missing.');
721
731
  msg.ack();
722
732
  }
723
733
  return;
package/src/protocol.js CHANGED
@@ -108,6 +108,19 @@ export function normalizeInboundPayload(payload) {
108
108
  };
109
109
  }
110
110
 
111
+ // Support for Join-Chat wakeup signals (BUG-26/29)
112
+ if (payload.type === 'wakeup') {
113
+ const jobId = payload.jobId || payload.id;
114
+ if (!jobId) return { ok: false, error: 'Missing jobId in wakeup payload' };
115
+ return {
116
+ ok: true,
117
+ job: {
118
+ id: jobId,
119
+ type: 'wakeup'
120
+ }
121
+ };
122
+ }
123
+
111
124
  // Fallback for legacy / generic JSON structures
112
125
  const task = payload.task ?? payload.prompt ?? payload.content ?? '';
113
126
  if (!task) {