@gholl-studio/pier-connector 0.1.6 → 0.1.8

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.
Files changed (2) hide show
  1. package/package.json +1 -1
  2. package/src/index.js +87 -70
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "@gholl-studio/pier-connector",
3
3
  "author": "gholl",
4
- "version": "0.1.6",
4
+ "version": "0.1.8",
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
@@ -162,7 +162,9 @@ export default function register(api) {
162
162
  OriginatingChannel: 'pier',
163
163
  OriginatingTo: `pier:${jobId}`,
164
164
  WasMentioned: true,
165
- CommandAuthorized: true
165
+ CommandAuthorized: true,
166
+ SystemPrompt: inbound.systemPrompt || undefined,
167
+ MessageId: inbound.messageId || jobId
166
168
  });
167
169
 
168
170
  // Create a dispatcher to handle the reply lifecycle (fixes sendFinalReply error)
@@ -173,9 +175,12 @@ export default function register(api) {
173
175
  logger.debug(`[pier-connector] Dispatcher idle for session ${route.sessionKey}`);
174
176
  },
175
177
  deliver: async (payload) => {
176
- // This triggers the outbound.sendText handler we defined in pierChannel
177
- await api.runtime.channel.reply.withReplyDispatcher(dispatcher, async () => {
178
- // withReplyDispatcher is sometimes needed by internal SDK methods
178
+ // Route Agent's reply to outbound.sendText so it reaches NATS
179
+ logger.debug(`[pier-connector] deliver() called with text: "${truncate(payload.text, 60)}"`);
180
+ await pierChannel.outbound.sendText({
181
+ text: payload.text,
182
+ to: `pier:${jobId}`,
183
+ metadata: activeNodeJobs.get(jobId),
179
184
  });
180
185
  }
181
186
  });
@@ -195,12 +200,14 @@ export default function register(api) {
195
200
  }
196
201
 
197
202
  try {
203
+ logger.info(`[pier-connector] 🧠 Triggering agent for session ${route.sessionKey}...`);
198
204
  // Dispatch reply — this will trigger outbound: sendText in pierChannel
199
205
  await api.runtime.channel.reply.dispatchReplyFromConfig({
200
206
  ctx: ctxPayload,
201
207
  cfg: api.config,
202
208
  dispatcher
203
209
  });
210
+ logger.info(`[pier-connector] 🧠 Agent dispatch completed for ${route.sessionKey}`);
204
211
  } finally {
205
212
  markDispatchIdle();
206
213
  }
@@ -269,11 +276,14 @@ export default function register(api) {
269
276
  const text = ctx.text;
270
277
  let metadata = ctx.metadata;
271
278
 
279
+ logger.info(`[pier-connector] 📤 Agent sending reply: "${truncate(text, 40)}" (To: ${ctx.to})`);
280
+
272
281
  if (!metadata && ctx.to) {
273
282
  const toId = ctx.to.replace(/^pier:/, '');
274
283
  metadata = activeNodeJobs.get(toId);
284
+ logger.debug(`[pier-connector] 📤 Resolved metadata for ${toId}: ${metadata ? 'Found' : 'Missing'}`);
275
285
  }
276
-
286
+
277
287
  const jobId = metadata?.pierJobId;
278
288
  const msg = metadata?.pierNatsMsg;
279
289
  const isRealtimeMsg = metadata?.isRealtimeMsg;
@@ -306,14 +316,18 @@ export default function register(api) {
306
316
  } catch (err) {
307
317
  logger.error(`[pier-connector] Failed to send realtime reply: ${err.message}`);
308
318
  }
309
- } else if (msg) {
310
- safeRespond(msg, responsePayload);
311
-
312
- jobsCompleted++;
313
- logger.info(
314
- `[pier-connector] ✔ Job ${jobId} completed` +
315
- (elapsed ? ` latency: ${elapsed}ms` : ''),
316
- );
319
+ } else if (js) {
320
+ // Publish result to jobs.submit via JetStream (not msg.respond)
321
+ try {
322
+ await js.publish('jobs.submit', new TextEncoder().encode(JSON.stringify(responsePayload)));
323
+ jobsCompleted++;
324
+ logger.info(
325
+ `[pier-connector] Job ${jobId} result published to jobs.submit` +
326
+ (elapsed ? ` — latency: ${elapsed}ms` : ''),
327
+ );
328
+ } catch (err) {
329
+ logger.error(`[pier-connector] ❌ Failed to publish result for job ${jobId}: ${err.message}`);
330
+ }
317
331
  }
318
332
 
319
333
  // Delayed stop for job-specific message listener
@@ -468,17 +482,35 @@ export default function register(api) {
468
482
 
469
483
  (async () => {
470
484
  try {
471
- let consumer = await js.consumers.get(streamName, durableName).catch(async () => {
485
+ // SDK-5: Try to get existing consumer; if config changed, delete and recreate
486
+ let consumer;
487
+ try {
488
+ consumer = await js.consumers.get(streamName, durableName);
489
+ } catch {
490
+ // Consumer doesn't exist yet, create it
472
491
  logger.info(`[pier-connector] Creating new Marketplace Consumer: ${durableName}`);
473
- await jsm.consumers.add(streamName, {
474
- durable_name: durableName,
475
- filter_subject: publicSubject,
476
- deliver_policy: DeliverPolicy.All,
477
- ack_policy: AckPolicy.Explicit,
478
- deliver_group: config.queueGroup
479
- });
480
- return await js.consumers.get(streamName, durableName);
481
- });
492
+ try {
493
+ await jsm.consumers.add(streamName, {
494
+ durable_name: durableName,
495
+ filter_subject: publicSubject,
496
+ deliver_policy: DeliverPolicy.New, // SDK-3: Only new messages
497
+ ack_policy: AckPolicy.Explicit,
498
+ deliver_group: config.queueGroup
499
+ });
500
+ } catch (addErr) {
501
+ // SDK-5: Config mismatch — delete old and recreate
502
+ logger.warn(`[pier-connector] Consumer config conflict, recreating: ${addErr.message}`);
503
+ try { await jsm.consumers.delete(streamName, durableName); } catch { /* ignore */ }
504
+ await jsm.consumers.add(streamName, {
505
+ durable_name: durableName,
506
+ filter_subject: publicSubject,
507
+ deliver_policy: DeliverPolicy.New,
508
+ ack_policy: AckPolicy.Explicit,
509
+ deliver_group: config.queueGroup
510
+ });
511
+ }
512
+ consumer = await js.consumers.get(streamName, durableName);
513
+ }
482
514
 
483
515
  const iter = await consumer.consume();
484
516
  for await (const msg of iter) {
@@ -502,16 +534,31 @@ export default function register(api) {
502
534
 
503
535
  (async () => {
504
536
  try {
505
- let consumer = await js.consumers.get(streamName, durableName).catch(async () => {
537
+ // SDK-5: Try to get existing consumer; if config changed, delete and recreate
538
+ let consumer;
539
+ try {
540
+ consumer = await js.consumers.get(streamName, durableName);
541
+ } catch {
506
542
  logger.info(`[pier-connector] Creating new Direct Consumer: ${durableName}`);
507
- await jsm.consumers.add(streamName, {
508
- durable_name: durableName,
509
- filter_subject: privateSubject,
510
- deliver_policy: DeliverPolicy.All,
511
- ack_policy: AckPolicy.Explicit
512
- });
513
- return await js.consumers.get(streamName, durableName);
514
- });
543
+ try {
544
+ await jsm.consumers.add(streamName, {
545
+ durable_name: durableName,
546
+ filter_subject: privateSubject,
547
+ deliver_policy: DeliverPolicy.New, // SDK-4: Only new messages
548
+ ack_policy: AckPolicy.Explicit
549
+ });
550
+ } catch (addErr) {
551
+ logger.warn(`[pier-connector] Consumer config conflict, recreating: ${addErr.message}`);
552
+ try { await jsm.consumers.delete(streamName, durableName); } catch { /* ignore */ }
553
+ await jsm.consumers.add(streamName, {
554
+ durable_name: durableName,
555
+ filter_subject: privateSubject,
556
+ deliver_policy: DeliverPolicy.New,
557
+ ack_policy: AckPolicy.Explicit
558
+ });
559
+ }
560
+ consumer = await js.consumers.get(streamName, durableName);
561
+ }
515
562
 
516
563
  const iter = await consumer.consume();
517
564
  for await (const msg of iter) {
@@ -546,9 +593,9 @@ export default function register(api) {
546
593
  const cInfo = await jsm.consumers.add(streamName, {
547
594
  durable_name: durableName,
548
595
  filter_subject: msgSubject,
549
- deliver_policy: DeliverPolicy.All,
596
+ deliver_policy: DeliverPolicy.New, // Only new messages to avoid replay spam
550
597
  ack_policy: AckPolicy.Explicit,
551
- ack_wait: 30000, // 30s ack wait
598
+ ack_wait: 30000,
552
599
  });
553
600
  const consumer = await js.consumers.get(streamName, cInfo.name);
554
601
 
@@ -556,31 +603,7 @@ export default function register(api) {
556
603
  jobSubscriptions.set(jobId, iter);
557
604
 
558
605
  (async () => {
559
- let historyBuffer = [];
560
- let isBursting = true;
561
-
562
- // Small delay to collect initial "DeliverPolicy.All" burst
563
- const burstTimeout = setTimeout(() => {
564
- if (isBursting && historyBuffer.length > 0) {
565
- isBursting = false;
566
- logger.info(`[pier-connector] 📦 Batch processing ${historyBuffer.length} historical messages for ${jobId}`);
567
-
568
- const combinedHistory = historyBuffer.map(m => `User: ${m.content}`).join('\n');
569
- const lastSenderId = historyBuffer[historyBuffer.length - 1].sender_id;
570
-
571
- receiveIncoming({
572
- accountId: config.accountId || 'default',
573
- senderId: `pier:${lastSenderId}`,
574
- text: `[Conversation History]\n${combinedHistory}\n\nPlease continue based on the above history.`,
575
- }, jobId).catch(err => {
576
- logger.error(`[pier-connector] Failed to process batch history: ${err.message}`);
577
- });
578
-
579
- historyBuffer = [];
580
- } else {
581
- isBursting = false;
582
- }
583
- }, 1000); // 1s burst window
606
+ logger.info(`[pier-connector] 👂 Monitoring new messages for job ${jobId}`);
584
607
 
585
608
  for await (const msg of iter) {
586
609
  try {
@@ -613,21 +636,15 @@ export default function register(api) {
613
636
  msg_id: msgPayload.id,
614
637
  reader_id: config.nodeId
615
638
  })));
616
- logger.info(`[pier-connector] 👁️ Sent read receipt for message ${msgPayload.id}`);
639
+ logger.debug(`[pier-connector] 👁️ Sent read receipt for message ${msgPayload.id}`);
617
640
  } catch (err) {
618
641
  logger.error(`[pier-connector] Failed to send read receipt: ${err.message}`);
619
642
  }
620
643
  }
621
644
 
622
645
  const content = msgPayload.content;
623
-
624
- if (isBursting) {
625
- historyBuffer.push(msgPayload);
626
- msg.ack();
627
- continue;
628
- }
629
-
630
- logger.info(`[pier-connector] 💬 Message for job ${jobId}: "${truncate(content, 40)}"`);
646
+ logger.info(`[pier-connector] 📥 Incoming message for ${jobId}: "${truncate(content, 40)}"`);
647
+
631
648
  const senderCore = msgPayload.sender_id;
632
649
 
633
650
  activeNodeJobs.set(jobId, {
@@ -635,7 +652,7 @@ export default function register(api) {
635
652
  isRealtimeMsg: true
636
653
  });
637
654
 
638
- // Wait for agent to finish before acking - this provides backpressure
655
+ // Trigger agent
639
656
  await receiveIncoming({
640
657
  accountId: config.accountId || 'default',
641
658
  senderId: `pier:${senderCore}`,
@@ -923,7 +940,7 @@ export default function register(api) {
923
940
  method: 'POST',
924
941
  headers: {
925
942
  'Content-Type': 'application/json',
926
- 'X-API-Key': config.secretKey // Assuming secretKey can be used as API key for node-specific actions
943
+ 'Authorization': `Bearer ${config.secretKey}` // BUG-7: Use Authorization header, not X-API-Key
927
944
  },
928
945
  body: JSON.stringify({
929
946
  sender_id: config.nodeId,