@gholl-studio/pier-connector 0.2.16 → 0.2.18
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 +1 -1
- package/src/index.js +53 -51
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.
|
|
4
|
+
"version": "0.2.18",
|
|
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
|
@@ -188,13 +188,25 @@ export default function register(api) {
|
|
|
188
188
|
}
|
|
189
189
|
});
|
|
190
190
|
|
|
191
|
+
const dynamicSessionKey = `pier-job-${jobId}`;
|
|
192
|
+
const injectedPrompt = [
|
|
193
|
+
"=========================================",
|
|
194
|
+
"CRITICAL DIRECTIVE - PIER TRADING NETWORK",
|
|
195
|
+
"=========================================",
|
|
196
|
+
"You are an autonomous AI worker on Pier.",
|
|
197
|
+
"1. You MUST USE the `pier_finish_task` tool to submit the final result when your work is complete.",
|
|
198
|
+
"2. If you need to negotiate or clarify requirements with the employer, use `pier_chat`.",
|
|
199
|
+
"3. Do not just wait in silence. Always officially finish the task if the employer's goal is met.",
|
|
200
|
+
inbound.systemPrompt ? `\nEmployer Guidelines:\n${inbound.systemPrompt}` : ""
|
|
201
|
+
].join('\n');
|
|
202
|
+
|
|
191
203
|
const ctxPayload = api.runtime.channel.reply.finalizeInboundContext({
|
|
192
204
|
Body: inbound.text,
|
|
193
205
|
BodyForAgent: inbound.text,
|
|
194
206
|
RawBody: inbound.text,
|
|
195
207
|
From: inbound.senderId,
|
|
196
208
|
To: `pier:${jobId}`,
|
|
197
|
-
SessionKey:
|
|
209
|
+
SessionKey: dynamicSessionKey,
|
|
198
210
|
AccountId: route.accountId,
|
|
199
211
|
ChatType: 'direct',
|
|
200
212
|
SenderId: inbound.senderId,
|
|
@@ -204,7 +216,7 @@ export default function register(api) {
|
|
|
204
216
|
OriginatingTo: `pier:${jobId}`,
|
|
205
217
|
WasMentioned: true,
|
|
206
218
|
CommandAuthorized: true,
|
|
207
|
-
SystemPrompt:
|
|
219
|
+
SystemPrompt: injectedPrompt,
|
|
208
220
|
MessageId: inbound.messageId || jobId
|
|
209
221
|
});
|
|
210
222
|
|
|
@@ -215,7 +227,7 @@ export default function register(api) {
|
|
|
215
227
|
cfg: api.config,
|
|
216
228
|
agentId: route.agentId,
|
|
217
229
|
onIdle: () => {
|
|
218
|
-
logger.debug(`[pier-connector] Dispatcher idle for session ${
|
|
230
|
+
logger.debug(`[pier-connector] Dispatcher idle for session ${dynamicSessionKey}`);
|
|
219
231
|
},
|
|
220
232
|
deliver: async (payload) => {
|
|
221
233
|
const metadata = activeNodeJobs.get(jobId);
|
|
@@ -239,10 +251,10 @@ export default function register(api) {
|
|
|
239
251
|
// Record session meta
|
|
240
252
|
if (api.runtime.channel.session?.recordSessionMetaFromInbound) {
|
|
241
253
|
try {
|
|
242
|
-
const storePath = api.runtime.channel.session.resolveStorePath(api.config,
|
|
254
|
+
const storePath = api.runtime.channel.session.resolveStorePath(api.config, dynamicSessionKey);
|
|
243
255
|
await api.runtime.channel.session.recordSessionMetaFromInbound({
|
|
244
256
|
storePath,
|
|
245
|
-
sessionKey:
|
|
257
|
+
sessionKey: dynamicSessionKey,
|
|
246
258
|
ctx: ctxPayload
|
|
247
259
|
});
|
|
248
260
|
} catch (err) {
|
|
@@ -251,14 +263,14 @@ export default function register(api) {
|
|
|
251
263
|
}
|
|
252
264
|
|
|
253
265
|
try {
|
|
254
|
-
logger.info(`[pier-connector] 🧠 Triggering agent for session ${
|
|
266
|
+
logger.info(`[pier-connector] 🧠 Triggering agent for session ${dynamicSessionKey}...`);
|
|
255
267
|
// Dispatch reply — this will trigger outbound: sendText in pierChannel
|
|
256
268
|
await api.runtime.channel.reply.dispatchReplyFromConfig({
|
|
257
269
|
ctx: ctxPayload,
|
|
258
270
|
cfg: api.config,
|
|
259
271
|
dispatcher
|
|
260
272
|
});
|
|
261
|
-
logger.info(`[pier-connector] 🧠 Agent dispatch completed for ${
|
|
273
|
+
logger.info(`[pier-connector] 🧠 Agent dispatch completed for ${dynamicSessionKey}`);
|
|
262
274
|
} finally {
|
|
263
275
|
markDispatchIdle();
|
|
264
276
|
}
|
|
@@ -339,42 +351,26 @@ export default function register(api) {
|
|
|
339
351
|
const msg = metadata?.pierNatsMsg;
|
|
340
352
|
const isRealtimeMsg = metadata?.isRealtimeMsg;
|
|
341
353
|
|
|
342
|
-
if (
|
|
343
|
-
|
|
344
|
-
|
|
345
|
-
|
|
346
|
-
|
|
347
|
-
|
|
348
|
-
|
|
349
|
-
|
|
350
|
-
|
|
351
|
-
|
|
352
|
-
|
|
353
|
-
|
|
354
|
-
|
|
355
|
-
|
|
356
|
-
|
|
357
|
-
|
|
358
|
-
|
|
359
|
-
|
|
360
|
-
|
|
361
|
-
|
|
362
|
-
sender_id: config.nodeId,
|
|
363
|
-
sender_type: 'node',
|
|
364
|
-
content: text,
|
|
365
|
-
timestamp: new Date().toISOString(),
|
|
366
|
-
auth_token: config.secretKey // Secure token for backend validation
|
|
367
|
-
};
|
|
368
|
-
|
|
369
|
-
await js.publish(replySubject, new TextEncoder().encode(JSON.stringify(replyPayload)));
|
|
370
|
-
logger.info(`[pier-connector] 💬 Agent reply published directly to NATS for job ${jobId}`);
|
|
371
|
-
} catch (err) {
|
|
372
|
-
logger.error(`[pier-connector] Failed to publish realtime reply to NATS: ${err.message}`);
|
|
373
|
-
}
|
|
374
|
-
} else if (js) {
|
|
375
|
-
// BUG-44: Marketplace results are now buffered in receiveIncoming and submitted ONCE at the end.
|
|
376
|
-
// sendText only logs the progress here.
|
|
377
|
-
logger.debug(`[pier-connector] ⏳ Marketplace response buffered: "${truncate(text, 40)}"`);
|
|
354
|
+
if (jobId && js) { // Ensure we have a jobId and NATS JetStream is available
|
|
355
|
+
try {
|
|
356
|
+
const config = getActiveConfig();
|
|
357
|
+
const replySubject = `jobs.job.${jobId}.msg`;
|
|
358
|
+
|
|
359
|
+
// Treat ALL agent text outputs as Chat Messages so the Employer sees them in real-time
|
|
360
|
+
const chatPayload = {
|
|
361
|
+
id: crypto.randomUUID ? crypto.randomUUID() : (Math.random().toString(36).substring(2)),
|
|
362
|
+
job_id: jobId,
|
|
363
|
+
sender_id: config.nodeId || 'anonymous',
|
|
364
|
+
sender_type: 'node',
|
|
365
|
+
content: text,
|
|
366
|
+
timestamp: new Date().toISOString(),
|
|
367
|
+
auth_token: config.secretKey // Secure token for backend validation
|
|
368
|
+
};
|
|
369
|
+
|
|
370
|
+
await js.publish(replySubject, new TextEncoder().encode(JSON.stringify(chatPayload)));
|
|
371
|
+
logger.info(`[pier-connector] 💬 Agent reply published directly to NATS chat for job ${jobId}`);
|
|
372
|
+
} catch (err) {
|
|
373
|
+
logger.error(`[pier-connector] Failed to publish realtime reply to NATS: ${err.message}`);
|
|
378
374
|
}
|
|
379
375
|
|
|
380
376
|
// Delayed stop for job-specific message listener
|
|
@@ -546,9 +542,11 @@ export default function register(api) {
|
|
|
546
542
|
logger.warn(`[pier-connector] ⚠️ Failed to restore active jobs: ${err.message}`);
|
|
547
543
|
}
|
|
548
544
|
|
|
549
|
-
// Public pool with
|
|
545
|
+
// Public pool with broadcast via JetStream Durable Consumer
|
|
550
546
|
if (publicSubject) {
|
|
551
|
-
|
|
547
|
+
// For Bidding, every node needs to see the marketplace requests to evaluate them,
|
|
548
|
+
// so we do not use the shared queueGroup. We use a unique durable per node.
|
|
549
|
+
const durableName = `pier_market_${config.nodeId.replace(/-/g, '_')}`;
|
|
552
550
|
const streamName = 'PIER_JOBS';
|
|
553
551
|
logger.info(`[pier-connector] 👂 Listening to Marketplace (JetStream): ${publicSubject} (Durable: ${durableName})`);
|
|
554
552
|
|
|
@@ -567,7 +565,7 @@ export default function register(api) {
|
|
|
567
565
|
filter_subject: publicSubject,
|
|
568
566
|
deliver_policy: DeliverPolicy.New, // SDK-3: Only new messages
|
|
569
567
|
ack_policy: AckPolicy.Explicit,
|
|
570
|
-
deliver_group
|
|
568
|
+
// Removed deliver_group to ensure it's a broadcast to all independent nodes
|
|
571
569
|
});
|
|
572
570
|
} catch (addErr) {
|
|
573
571
|
// SDK-5: Config mismatch — delete old and recreate
|
|
@@ -578,7 +576,6 @@ export default function register(api) {
|
|
|
578
576
|
filter_subject: publicSubject,
|
|
579
577
|
deliver_policy: DeliverPolicy.New,
|
|
580
578
|
ack_policy: AckPolicy.Explicit,
|
|
581
|
-
deliver_group: config.queueGroup
|
|
582
579
|
});
|
|
583
580
|
}
|
|
584
581
|
consumer = await js.consumers.get(streamName, durableName);
|
|
@@ -809,6 +806,7 @@ export default function register(api) {
|
|
|
809
806
|
|
|
810
807
|
// Anti-Interference: If busy, ignore new marketplace jobs (BUG-34)
|
|
811
808
|
if (isBusy) {
|
|
809
|
+
logger.debug(`[pier-connector] 🚫 Busy. Ignoring new job payload: ${rawData.substring(0, 50)}...`);
|
|
812
810
|
// NAK so others in the queue group can take it
|
|
813
811
|
msg.nak();
|
|
814
812
|
return;
|
|
@@ -821,10 +819,12 @@ export default function register(api) {
|
|
|
821
819
|
return;
|
|
822
820
|
}
|
|
823
821
|
|
|
824
|
-
// For Marketplace jobs
|
|
825
|
-
//
|
|
822
|
+
// For Marketplace jobs: if assigned_node_id IS NOT present, it's open!
|
|
823
|
+
// Do NOT claim it, let the agent evaluate and use pier_bid_task instead.
|
|
826
824
|
const jobIdToClaim = payload.id;
|
|
827
|
-
|
|
825
|
+
const isTargeted = !!(payload.assigned_node_id || payload.targetNodeId || payload.TargetNodeID);
|
|
826
|
+
|
|
827
|
+
if (jobIdToClaim && isTargeted) {
|
|
828
828
|
const claimResult = await claimJob(jobIdToClaim);
|
|
829
829
|
if (!claimResult.ok) {
|
|
830
830
|
if (claimResult.alreadyClaimed) {
|
|
@@ -838,7 +838,8 @@ export default function register(api) {
|
|
|
838
838
|
}
|
|
839
839
|
}
|
|
840
840
|
|
|
841
|
-
//
|
|
841
|
+
// For an open bidding job, we don't lock the DB but we do set the node `isBusy` temporarily
|
|
842
|
+
// while it thinks whether to bid or not.
|
|
842
843
|
isBusy = true;
|
|
843
844
|
msg.ack();
|
|
844
845
|
|
|
@@ -1142,6 +1143,7 @@ export default function register(api) {
|
|
|
1142
1143
|
};
|
|
1143
1144
|
|
|
1144
1145
|
// For Node (Worker)
|
|
1146
|
+
registerSystemActionTool('pier_bid_task', 'Bid on an open marketplace task to offer your services to the employer', 'task_bid', { message: { type: 'string', description: 'Your pitch explaining why you are a good fit for this task' } });
|
|
1145
1147
|
registerSystemActionTool('pier_accept_task', 'Accept an offered task from the employer in the current chat', 'task_accept', {});
|
|
1146
1148
|
registerSystemActionTool('pier_finish_task', 'Submit the final result for a task', 'task_submit', { result: { type: 'string', description: 'The final result or file links' } });
|
|
1147
1149
|
|