@agrentingai/paperclip-adapter 0.2.2 → 0.4.0
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/CHANGELOG.md +86 -0
- package/LICENSE +21 -0
- package/README.md +158 -243
- package/VERSION +1 -0
- package/dist/server/index.cjs +698 -32
- package/dist/server/index.cjs.map +1 -1
- package/dist/server/index.d.cts +217 -32
- package/dist/server/index.d.ts +217 -32
- package/dist/server/index.js +677 -32
- package/dist/server/index.js.map +1 -1
- package/package.json +25 -7
- package/server/src/adapter.test.ts +0 -497
- package/server/src/adapter.ts +0 -1044
- package/server/src/balance-monitor.test.ts +0 -147
- package/server/src/balance-monitor.ts +0 -118
- package/server/src/client.test.ts +0 -949
- package/server/src/client.ts +0 -550
- package/server/src/comment-sync.test.ts +0 -25
- package/server/src/comment-sync.ts +0 -71
- package/server/src/crypto.test.ts +0 -62
- package/server/src/crypto.ts +0 -25
- package/server/src/index.ts +0 -67
- package/server/src/polling.test.ts +0 -208
- package/server/src/polling.ts +0 -183
- package/server/src/types.ts +0 -244
- package/server/src/webhook-handler.test.ts +0 -379
- package/server/src/webhook-handler.ts +0 -292
- package/ui/src/adapter.ts +0 -131
- package/ui/src/index.ts +0 -2
package/dist/server/index.cjs
CHANGED
|
@@ -33,12 +33,20 @@ __export(index_exports, {
|
|
|
33
33
|
AgrentingClient: () => AgrentingClient,
|
|
34
34
|
MAX_POLLS: () => MAX_POLLS,
|
|
35
35
|
POLL_INTERVALS_MS: () => POLL_INTERVALS_MS,
|
|
36
|
+
agentConfigurationDoc: () => agentConfigurationDoc,
|
|
37
|
+
agrentingAppGalleryEntry: () => agrentingAppGalleryEntry,
|
|
36
38
|
autoSelectAgent: () => autoSelectAgent,
|
|
37
39
|
canSubmitTask: () => canSubmitTask,
|
|
40
|
+
cancel: () => cancel,
|
|
41
|
+
cancelHiring: () => cancelHiring,
|
|
42
|
+
cancelTask: () => cancelTask,
|
|
38
43
|
checkBalance: () => checkBalance,
|
|
39
44
|
createServerAdapter: () => createServerAdapter,
|
|
40
45
|
createWebhookHandler: () => createWebhookHandler,
|
|
41
46
|
deregisterWebhook: () => deregisterWebhook,
|
|
47
|
+
detectModel: () => detectModel,
|
|
48
|
+
execute: () => execute,
|
|
49
|
+
executePaperclip: () => executePaperclip,
|
|
42
50
|
executeWithRetry: () => executeWithRetry,
|
|
43
51
|
formatAgentResponse: () => formatAgentResponse,
|
|
44
52
|
formatInsufficientBalanceComment: () => formatInsufficientBalanceComment,
|
|
@@ -47,13 +55,20 @@ __export(index_exports, {
|
|
|
47
55
|
getActiveTaskMappings: () => getActiveTaskMappings,
|
|
48
56
|
getAgentProfile: () => getAgentProfile,
|
|
49
57
|
getBackoffMs: () => getBackoffMs,
|
|
58
|
+
getConfigSchema: () => getConfigSchema,
|
|
50
59
|
getHiring: () => getHiring,
|
|
51
60
|
getHiringMessages: () => getHiringMessages,
|
|
61
|
+
getPaperclipConfigSchema: () => getPaperclipConfigSchema,
|
|
52
62
|
getTaskMessages: () => getTaskMessages,
|
|
63
|
+
getTaskProgress: () => getTaskProgress,
|
|
53
64
|
getWebhookGracePeriodMs: () => getWebhookGracePeriodMs,
|
|
54
65
|
hireAgent: () => hireAgent,
|
|
66
|
+
invoke: () => invoke,
|
|
67
|
+
label: () => label,
|
|
55
68
|
listCapabilities: () => listCapabilities,
|
|
56
69
|
listHirings: () => listHirings,
|
|
70
|
+
listSkills: () => listSkills,
|
|
71
|
+
paperclipSessionCodec: () => paperclipSessionCodec,
|
|
57
72
|
pollTaskUntilDone: () => pollTaskUntilDone,
|
|
58
73
|
processIncomingMessage: () => processIncomingMessage,
|
|
59
74
|
reassignTask: () => reassignTask,
|
|
@@ -62,6 +77,12 @@ __export(index_exports, {
|
|
|
62
77
|
retryHiring: () => retryHiring,
|
|
63
78
|
sendMessageToHiring: () => sendMessageToHiring,
|
|
64
79
|
sendMessageToTask: () => sendMessageToTask,
|
|
80
|
+
sessionCodec: () => sessionCodec,
|
|
81
|
+
status: () => status,
|
|
82
|
+
syncSkills: () => syncSkills,
|
|
83
|
+
testEnvironment: () => testEnvironment,
|
|
84
|
+
testPaperclipEnvironment: () => testPaperclipEnvironment,
|
|
85
|
+
type: () => type,
|
|
65
86
|
unregisterTaskMapping: () => unregisterTaskMapping,
|
|
66
87
|
verifyWebhookSignature: () => verifyWebhookSignature
|
|
67
88
|
});
|
|
@@ -70,6 +91,12 @@ module.exports = __toCommonJS(index_exports);
|
|
|
70
91
|
// server/src/client.ts
|
|
71
92
|
var DEFAULT_TIMEOUT_MS = 3e4;
|
|
72
93
|
var MAX_RETRIES = 3;
|
|
94
|
+
var NonRetryableAgrentingError = class extends Error {
|
|
95
|
+
constructor(message) {
|
|
96
|
+
super(message);
|
|
97
|
+
this.name = "NonRetryableAgrentingError";
|
|
98
|
+
}
|
|
99
|
+
};
|
|
73
100
|
var AgrentingClient = class {
|
|
74
101
|
baseUrl;
|
|
75
102
|
apiKey;
|
|
@@ -116,17 +143,22 @@ var AgrentingClient = class {
|
|
|
116
143
|
await new Promise((resolve) => setTimeout(resolve, delayMs));
|
|
117
144
|
continue;
|
|
118
145
|
}
|
|
119
|
-
throw new
|
|
146
|
+
throw new NonRetryableAgrentingError(
|
|
120
147
|
`Agrenting API ${response.status}: ${text.slice(0, 500)}`
|
|
121
148
|
);
|
|
122
149
|
}
|
|
123
150
|
const envelope = await response.json();
|
|
124
151
|
if (Array.isArray(envelope.errors) && envelope.errors.length) {
|
|
125
|
-
throw new
|
|
152
|
+
throw new NonRetryableAgrentingError(
|
|
153
|
+
`API errors: ${envelope.errors.join(", ")}`
|
|
154
|
+
);
|
|
126
155
|
}
|
|
127
156
|
return envelope.data ?? envelope;
|
|
128
157
|
} catch (err) {
|
|
129
158
|
lastError = err instanceof Error ? err : new Error(String(err));
|
|
159
|
+
if (lastError instanceof NonRetryableAgrentingError) {
|
|
160
|
+
throw lastError;
|
|
161
|
+
}
|
|
130
162
|
if (attempt < MAX_RETRIES) {
|
|
131
163
|
clearTimeout(timer);
|
|
132
164
|
const delayMs = Math.min(1e3 * 2 ** attempt, 3e4);
|
|
@@ -312,12 +344,23 @@ var AgrentingClient = class {
|
|
|
312
344
|
async getAgentProfile(agentDid) {
|
|
313
345
|
return this.request("GET", `/api/v1/agents/${encodeURIComponent(agentDid)}`);
|
|
314
346
|
}
|
|
315
|
-
/**
|
|
316
|
-
*
|
|
347
|
+
/** Create a paid hiring for an agent.
|
|
348
|
+
* Agrenting requires a concrete task, capability, and offered price.
|
|
317
349
|
*/
|
|
318
|
-
async hireAgent(agentDid, options
|
|
319
|
-
const body = {
|
|
320
|
-
|
|
350
|
+
async hireAgent(agentDid, options) {
|
|
351
|
+
const body = {
|
|
352
|
+
task_description: options.taskDescription,
|
|
353
|
+
capability_requested: options.capabilityRequested,
|
|
354
|
+
price: String(options.price),
|
|
355
|
+
delivery_mode: options.deliveryMode ?? "output"
|
|
356
|
+
};
|
|
357
|
+
if (options.repoUrl) body.repo_url = options.repoUrl;
|
|
358
|
+
if (options.repoAccessToken) body.repo_access_token = options.repoAccessToken;
|
|
359
|
+
if (options.clientIdempotencyKey) {
|
|
360
|
+
body.client_idempotency_key = options.clientIdempotencyKey;
|
|
361
|
+
}
|
|
362
|
+
if (options.taskInput) body.task_input = options.taskInput;
|
|
363
|
+
if (options.clientMessage) body.client_message = options.clientMessage;
|
|
321
364
|
return this.request(
|
|
322
365
|
"POST",
|
|
323
366
|
`/api/v1/agents/${encodeURIComponent(agentDid)}/hire`,
|
|
@@ -376,13 +419,11 @@ var AgrentingClient = class {
|
|
|
376
419
|
);
|
|
377
420
|
}
|
|
378
421
|
/** Get messages for a hiring.
|
|
379
|
-
* GET /api/v1/hirings/:id
|
|
422
|
+
* The canonical API includes recent messages in GET /api/v1/hirings/:id.
|
|
380
423
|
*/
|
|
381
424
|
async getHiringMessages(hiringId) {
|
|
382
|
-
|
|
383
|
-
|
|
384
|
-
`/api/v1/hirings/${hiringId}/messages`
|
|
385
|
-
);
|
|
425
|
+
const hiring = await this.getHiring(hiringId);
|
|
426
|
+
return hiring.messages ?? [];
|
|
386
427
|
}
|
|
387
428
|
/** Retry a failed hiring.
|
|
388
429
|
* POST /api/v1/hirings/:id/retry
|
|
@@ -404,6 +445,15 @@ var AgrentingClient = class {
|
|
|
404
445
|
async getHiring(hiringId) {
|
|
405
446
|
return this.request("GET", `/api/v1/hirings/${hiringId}`);
|
|
406
447
|
}
|
|
448
|
+
/** Cancel an active hiring.
|
|
449
|
+
* POST /api/v1/hirings/:id/cancel
|
|
450
|
+
*/
|
|
451
|
+
async cancelHiring(hiringId) {
|
|
452
|
+
return this.request(
|
|
453
|
+
"POST",
|
|
454
|
+
`/api/v1/hirings/${hiringId}/cancel`
|
|
455
|
+
);
|
|
456
|
+
}
|
|
407
457
|
/** List hirings for the authenticated agent.
|
|
408
458
|
* GET /api/v1/hirings
|
|
409
459
|
*/
|
|
@@ -411,9 +461,16 @@ var AgrentingClient = class {
|
|
|
411
461
|
const params = new URLSearchParams();
|
|
412
462
|
if (options.status) params.set("status", options.status);
|
|
413
463
|
if (options.limit) params.set("limit", String(options.limit));
|
|
414
|
-
if (options.offset)
|
|
464
|
+
if (options.offset) {
|
|
465
|
+
const perPage = options.limit ?? 20;
|
|
466
|
+
params.set("page", String(Math.floor(options.offset / perPage) + 1));
|
|
467
|
+
}
|
|
415
468
|
const query = params.toString() ? `?${params}` : "";
|
|
416
|
-
|
|
469
|
+
const result = await this.request(
|
|
470
|
+
"GET",
|
|
471
|
+
`/api/v1/hirings${query}`
|
|
472
|
+
);
|
|
473
|
+
return result.hirings;
|
|
417
474
|
}
|
|
418
475
|
/** List agents filtered by capability for auto-select.
|
|
419
476
|
* GET /api/v1/agents?capability=X
|
|
@@ -426,6 +483,493 @@ var AgrentingClient = class {
|
|
|
426
483
|
}
|
|
427
484
|
};
|
|
428
485
|
|
|
486
|
+
// server/src/paperclip.ts
|
|
487
|
+
var type = "agrenting";
|
|
488
|
+
var label = "Agrenting";
|
|
489
|
+
var agentConfigurationDoc = `# agrenting agent configuration
|
|
490
|
+
|
|
491
|
+
Adapter: agrenting
|
|
492
|
+
|
|
493
|
+
Use when:
|
|
494
|
+
- A Paperclip agent should delegate each heartbeat to a remote agent hired from Agrenting.
|
|
495
|
+
- The operator has an Agrenting user API token and sufficient ledger balance.
|
|
496
|
+
|
|
497
|
+
Core fields:
|
|
498
|
+
- agrentingUrl (required): Agrenting base URL, normally https://agrenting.com
|
|
499
|
+
- apiKey (required, secret): Agrenting user API token (ap_...)
|
|
500
|
+
- agentDid (required): DID of the marketplace agent to hire
|
|
501
|
+
- capabilityRequested (optional): defaults to the first capability on the agent profile
|
|
502
|
+
- price (optional): defaults to the agent's current base price
|
|
503
|
+
- timeoutSec (optional): maximum time to poll a hiring, default 600
|
|
504
|
+
- pollIntervalMs (optional): hiring status poll interval, default 2000
|
|
505
|
+
- deliveryMode (optional): output by default; push requires repository access
|
|
506
|
+
- repoUrl (optional): repository URL used only when deliveryMode is push
|
|
507
|
+
|
|
508
|
+
Recommended API-key scopes:
|
|
509
|
+
- agents:discover, agents:read, hire:create, hirings:read, hirings:cancel
|
|
510
|
+
- add balance:read and artifacts:read when sharing the key with Apps/Claude
|
|
511
|
+
- deposits:create and account:read/account:write are optional elevated scopes
|
|
512
|
+
- set max_price_per_hire on the key to cap paid actions
|
|
513
|
+
|
|
514
|
+
Execution:
|
|
515
|
+
- Each Paperclip run creates one canonical Agrenting hiring.
|
|
516
|
+
- The Paperclip run id is sent as client_idempotency_key so safe retries deduplicate.
|
|
517
|
+
- Paperclip polls the hiring until it completes, fails, is cancelled, or times out.
|
|
518
|
+
- Push delivery can use a repository URL from Paperclip and an Agrenting-stored
|
|
519
|
+
GitHub token. The adapter never stores a repository token in agent config.
|
|
520
|
+
`;
|
|
521
|
+
var TERMINAL_STATUSES = /* @__PURE__ */ new Set([
|
|
522
|
+
"completed",
|
|
523
|
+
"failed",
|
|
524
|
+
"cancelled",
|
|
525
|
+
"disputed",
|
|
526
|
+
"refunded"
|
|
527
|
+
]);
|
|
528
|
+
var DEFAULT_TIMEOUT_SEC = 600;
|
|
529
|
+
var DEFAULT_POLL_INTERVAL_MS = 2e3;
|
|
530
|
+
var MAX_TASK_DESCRIPTION_LENGTH = 5e3;
|
|
531
|
+
function asRecord(value) {
|
|
532
|
+
if (typeof value !== "object" || value === null || Array.isArray(value)) {
|
|
533
|
+
return null;
|
|
534
|
+
}
|
|
535
|
+
return value;
|
|
536
|
+
}
|
|
537
|
+
function nonEmpty(value) {
|
|
538
|
+
return typeof value === "string" && value.trim().length > 0 ? value.trim() : null;
|
|
539
|
+
}
|
|
540
|
+
function positiveNumber(value, fallback) {
|
|
541
|
+
const parsed = typeof value === "number" ? value : Number(value);
|
|
542
|
+
return Number.isFinite(parsed) && parsed > 0 ? parsed : fallback;
|
|
543
|
+
}
|
|
544
|
+
function configFrom(raw) {
|
|
545
|
+
return {
|
|
546
|
+
agrentingUrl: nonEmpty(raw.agrentingUrl) ?? "https://agrenting.com",
|
|
547
|
+
apiKey: nonEmpty(raw.apiKey) ?? "",
|
|
548
|
+
agentDid: nonEmpty(raw.agentDid) ?? "",
|
|
549
|
+
capabilityRequested: nonEmpty(raw.capabilityRequested) ?? void 0,
|
|
550
|
+
price: nonEmpty(raw.price) ?? void 0,
|
|
551
|
+
timeoutSec: positiveNumber(raw.timeoutSec, DEFAULT_TIMEOUT_SEC),
|
|
552
|
+
pollIntervalMs: positiveNumber(
|
|
553
|
+
raw.pollIntervalMs,
|
|
554
|
+
DEFAULT_POLL_INTERVAL_MS
|
|
555
|
+
),
|
|
556
|
+
deliveryMode: raw.deliveryMode === "push" ? "push" : "output",
|
|
557
|
+
repoUrl: nonEmpty(raw.repoUrl) ?? void 0
|
|
558
|
+
};
|
|
559
|
+
}
|
|
560
|
+
function taskDescriptionFrom(ctx) {
|
|
561
|
+
const context = ctx.context;
|
|
562
|
+
const title = nonEmpty(context.taskTitle) ?? nonEmpty(context.issueTitle) ?? nonEmpty(context.title);
|
|
563
|
+
const body = nonEmpty(context.paperclipTaskMarkdown) ?? nonEmpty(context.taskBody) ?? nonEmpty(context.taskDescription) ?? nonEmpty(context.issueDescription) ?? nonEmpty(context.prompt) ?? nonEmpty(context.input);
|
|
564
|
+
let description = [title, body].filter(Boolean).join("\n\n").trim();
|
|
565
|
+
if (!description) {
|
|
566
|
+
description = `Continue the assigned Paperclip work for ${ctx.agent.name}.`;
|
|
567
|
+
}
|
|
568
|
+
if (description.length <= MAX_TASK_DESCRIPTION_LENGTH) return description;
|
|
569
|
+
return `${description.slice(0, MAX_TASK_DESCRIPTION_LENGTH - 32)}
|
|
570
|
+
|
|
571
|
+
[truncated by Paperclip adapter]`;
|
|
572
|
+
}
|
|
573
|
+
function capabilityFrom(config, context, profile) {
|
|
574
|
+
return nonEmpty(config.capabilityRequested) ?? nonEmpty(context.capabilityRequested) ?? nonEmpty(context.capability) ?? profile.capabilities.find((capability) => capability.trim().length > 0) ?? null;
|
|
575
|
+
}
|
|
576
|
+
function priceFrom(config, context, profile) {
|
|
577
|
+
return nonEmpty(config.price) ?? nonEmpty(context.price) ?? nonEmpty(context.maxPrice) ?? nonEmpty(profile.base_price);
|
|
578
|
+
}
|
|
579
|
+
function repoUrlFrom(ctx) {
|
|
580
|
+
const workspace = asRecord(ctx.context.paperclipWorkspace);
|
|
581
|
+
return nonEmpty(configFrom(ctx.config).repoUrl) ?? nonEmpty(workspace?.repoUrl) ?? void 0;
|
|
582
|
+
}
|
|
583
|
+
function taskInputFrom(ctx) {
|
|
584
|
+
const input = {
|
|
585
|
+
paperclip_run_id: ctx.runId,
|
|
586
|
+
paperclip_agent_id: ctx.agent.id,
|
|
587
|
+
paperclip_company_id: ctx.agent.companyId
|
|
588
|
+
};
|
|
589
|
+
const mappings = [
|
|
590
|
+
["paperclip_issue_id", ctx.context.issueId ?? ctx.context.taskId],
|
|
591
|
+
["paperclip_project_id", ctx.context.projectId],
|
|
592
|
+
["paperclip_wake_reason", ctx.context.wakeReason]
|
|
593
|
+
];
|
|
594
|
+
for (const [key, value] of mappings) {
|
|
595
|
+
const normalized = nonEmpty(value);
|
|
596
|
+
if (normalized) input[key] = normalized;
|
|
597
|
+
}
|
|
598
|
+
const wake = asRecord(ctx.context.paperclipWake);
|
|
599
|
+
if (wake) input.paperclip_wake = wake;
|
|
600
|
+
return input;
|
|
601
|
+
}
|
|
602
|
+
function outputText(hiring) {
|
|
603
|
+
const output = hiring.task_output;
|
|
604
|
+
if (typeof output === "string" && output.trim()) return output;
|
|
605
|
+
const record = asRecord(output);
|
|
606
|
+
if (record) {
|
|
607
|
+
for (const key of ["result", "output", "text", "summary"]) {
|
|
608
|
+
const direct = nonEmpty(record[key]);
|
|
609
|
+
if (direct) return direct;
|
|
610
|
+
}
|
|
611
|
+
return JSON.stringify(record, null, 2);
|
|
612
|
+
}
|
|
613
|
+
return `Agrenting hiring ${hiring.id} completed.`;
|
|
614
|
+
}
|
|
615
|
+
function firstLine(value) {
|
|
616
|
+
return value.split(/\r?\n/).map((line) => line.trim()).find(Boolean) ?? "Agrenting hiring completed";
|
|
617
|
+
}
|
|
618
|
+
function sleep(ms) {
|
|
619
|
+
return new Promise((resolve) => setTimeout(resolve, ms));
|
|
620
|
+
}
|
|
621
|
+
function resultForFailure(hiring, message) {
|
|
622
|
+
return {
|
|
623
|
+
exitCode: 1,
|
|
624
|
+
signal: null,
|
|
625
|
+
timedOut: false,
|
|
626
|
+
errorMessage: message,
|
|
627
|
+
errorCode: `agrenting_hiring_${hiring.status}`,
|
|
628
|
+
provider: "agrenting",
|
|
629
|
+
biller: "agrenting",
|
|
630
|
+
sessionParams: { hiringId: hiring.id },
|
|
631
|
+
sessionDisplayId: hiring.id,
|
|
632
|
+
resultJson: {
|
|
633
|
+
hiringId: hiring.id,
|
|
634
|
+
status: hiring.status,
|
|
635
|
+
failedReason: hiring.failed_reason ?? null
|
|
636
|
+
}
|
|
637
|
+
};
|
|
638
|
+
}
|
|
639
|
+
async function executePaperclip(ctx) {
|
|
640
|
+
const config = configFrom(ctx.config);
|
|
641
|
+
if (!config.apiKey || !config.agentDid) {
|
|
642
|
+
return {
|
|
643
|
+
exitCode: 1,
|
|
644
|
+
signal: null,
|
|
645
|
+
timedOut: false,
|
|
646
|
+
errorCode: "agrenting_config_invalid",
|
|
647
|
+
errorMessage: "Agrenting requires apiKey and agentDid."
|
|
648
|
+
};
|
|
649
|
+
}
|
|
650
|
+
const client = new AgrentingClient(config);
|
|
651
|
+
try {
|
|
652
|
+
const profile = await client.getAgentProfile(config.agentDid);
|
|
653
|
+
const capability = capabilityFrom(config, ctx.context, profile);
|
|
654
|
+
const price = priceFrom(config, ctx.context, profile);
|
|
655
|
+
if (!capability) {
|
|
656
|
+
throw new Error(
|
|
657
|
+
"No capabilityRequested was configured and the Agrenting agent profile has no capabilities."
|
|
658
|
+
);
|
|
659
|
+
}
|
|
660
|
+
if (!price) {
|
|
661
|
+
throw new Error(
|
|
662
|
+
"No price was configured and the Agrenting agent profile has no base_price."
|
|
663
|
+
);
|
|
664
|
+
}
|
|
665
|
+
const taskDescription = taskDescriptionFrom(ctx);
|
|
666
|
+
await ctx.onLog(
|
|
667
|
+
"stdout",
|
|
668
|
+
`[agrenting] Hiring ${config.agentDid} for ${capability} at ${price}
|
|
669
|
+
`
|
|
670
|
+
);
|
|
671
|
+
const created = await client.hireAgent(config.agentDid, {
|
|
672
|
+
taskDescription,
|
|
673
|
+
capabilityRequested: capability,
|
|
674
|
+
price,
|
|
675
|
+
deliveryMode: config.deliveryMode,
|
|
676
|
+
clientIdempotencyKey: ctx.runId,
|
|
677
|
+
taskInput: taskInputFrom(ctx),
|
|
678
|
+
repoUrl: repoUrlFrom(ctx)
|
|
679
|
+
});
|
|
680
|
+
const hiringId = created.hiring.id;
|
|
681
|
+
await ctx.onMeta?.({
|
|
682
|
+
adapterType: type,
|
|
683
|
+
command: "POST /api/v1/agents/:did/hire",
|
|
684
|
+
context: { hiringId, agentDid: config.agentDid, capability, price }
|
|
685
|
+
});
|
|
686
|
+
await ctx.onLog(
|
|
687
|
+
"stdout",
|
|
688
|
+
`[agrenting] Hiring ${hiringId} created with status ${created.hiring.status}
|
|
689
|
+
`
|
|
690
|
+
);
|
|
691
|
+
const timeoutMs = (config.timeoutSec ?? DEFAULT_TIMEOUT_SEC) * 1e3;
|
|
692
|
+
const pollIntervalMs = config.pollIntervalMs ?? DEFAULT_POLL_INTERVAL_MS;
|
|
693
|
+
const deadline = Date.now() + timeoutMs;
|
|
694
|
+
let hiring = created.hiring;
|
|
695
|
+
let lastStatus = hiring.status;
|
|
696
|
+
while (!TERMINAL_STATUSES.has(hiring.status) && Date.now() < deadline) {
|
|
697
|
+
await sleep(Math.min(pollIntervalMs, Math.max(1, deadline - Date.now())));
|
|
698
|
+
hiring = await client.getHiring(hiringId);
|
|
699
|
+
if (hiring.status !== lastStatus) {
|
|
700
|
+
lastStatus = hiring.status;
|
|
701
|
+
await ctx.onLog(
|
|
702
|
+
"stdout",
|
|
703
|
+
`[agrenting] Hiring ${hiringId} is ${hiring.status}
|
|
704
|
+
`
|
|
705
|
+
);
|
|
706
|
+
}
|
|
707
|
+
}
|
|
708
|
+
if (!TERMINAL_STATUSES.has(hiring.status)) {
|
|
709
|
+
try {
|
|
710
|
+
await client.cancelHiring(hiringId);
|
|
711
|
+
await ctx.onLog(
|
|
712
|
+
"stderr",
|
|
713
|
+
`[agrenting] Hiring ${hiringId} timed out and was cancelled
|
|
714
|
+
`
|
|
715
|
+
);
|
|
716
|
+
} catch (cancelError) {
|
|
717
|
+
await ctx.onLog(
|
|
718
|
+
"stderr",
|
|
719
|
+
`[agrenting] Hiring ${hiringId} timed out; cancellation failed: ${cancelError instanceof Error ? cancelError.message : String(cancelError)}
|
|
720
|
+
`
|
|
721
|
+
);
|
|
722
|
+
}
|
|
723
|
+
return {
|
|
724
|
+
exitCode: null,
|
|
725
|
+
signal: null,
|
|
726
|
+
timedOut: true,
|
|
727
|
+
errorCode: "agrenting_hiring_timeout",
|
|
728
|
+
errorMessage: `Agrenting hiring ${hiringId} timed out after ${config.timeoutSec ?? DEFAULT_TIMEOUT_SEC}s.`,
|
|
729
|
+
provider: "agrenting",
|
|
730
|
+
biller: "agrenting",
|
|
731
|
+
sessionParams: { hiringId },
|
|
732
|
+
sessionDisplayId: hiringId
|
|
733
|
+
};
|
|
734
|
+
}
|
|
735
|
+
if (hiring.status !== "completed") {
|
|
736
|
+
return resultForFailure(
|
|
737
|
+
hiring,
|
|
738
|
+
hiring.failed_reason ?? `Agrenting hiring ended with status ${hiring.status}.`
|
|
739
|
+
);
|
|
740
|
+
}
|
|
741
|
+
const output = outputText(hiring);
|
|
742
|
+
await ctx.onLog("stdout", `${output}
|
|
743
|
+
`);
|
|
744
|
+
const numericPrice = Number(hiring.price ?? price);
|
|
745
|
+
return {
|
|
746
|
+
exitCode: 0,
|
|
747
|
+
signal: null,
|
|
748
|
+
timedOut: false,
|
|
749
|
+
provider: "agrenting",
|
|
750
|
+
biller: "agrenting",
|
|
751
|
+
billingType: "fixed",
|
|
752
|
+
costUsd: Number.isFinite(numericPrice) ? numericPrice : null,
|
|
753
|
+
sessionParams: { hiringId },
|
|
754
|
+
sessionDisplayId: hiringId,
|
|
755
|
+
summary: firstLine(output),
|
|
756
|
+
resultJson: {
|
|
757
|
+
hiringId,
|
|
758
|
+
status: hiring.status,
|
|
759
|
+
taskOutput: hiring.task_output ?? null
|
|
760
|
+
}
|
|
761
|
+
};
|
|
762
|
+
} catch (error) {
|
|
763
|
+
const message = error instanceof Error ? error.message : String(error);
|
|
764
|
+
const transient = /\b(429|5\d\d)\b|rate.?limit|temporar|timeout/i.test(message);
|
|
765
|
+
await ctx.onLog("stderr", `[agrenting] ${message}
|
|
766
|
+
`);
|
|
767
|
+
return {
|
|
768
|
+
exitCode: 1,
|
|
769
|
+
signal: null,
|
|
770
|
+
timedOut: false,
|
|
771
|
+
errorCode: "agrenting_hiring_failed",
|
|
772
|
+
errorFamily: transient ? "transient_upstream" : null,
|
|
773
|
+
errorMessage: message,
|
|
774
|
+
provider: "agrenting",
|
|
775
|
+
biller: "agrenting"
|
|
776
|
+
};
|
|
777
|
+
}
|
|
778
|
+
}
|
|
779
|
+
function summarizeChecks(checks) {
|
|
780
|
+
if (checks.some((check) => check.level === "error")) return "fail";
|
|
781
|
+
if (checks.some((check) => check.level === "warn")) return "warn";
|
|
782
|
+
return "pass";
|
|
783
|
+
}
|
|
784
|
+
async function testPaperclipEnvironment(ctx) {
|
|
785
|
+
const config = configFrom(ctx.config);
|
|
786
|
+
const checks = [];
|
|
787
|
+
let parsedUrl = null;
|
|
788
|
+
try {
|
|
789
|
+
parsedUrl = new URL(config.agrentingUrl);
|
|
790
|
+
if (parsedUrl.protocol !== "http:" && parsedUrl.protocol !== "https:") {
|
|
791
|
+
parsedUrl = null;
|
|
792
|
+
}
|
|
793
|
+
} catch {
|
|
794
|
+
parsedUrl = null;
|
|
795
|
+
}
|
|
796
|
+
if (!parsedUrl) {
|
|
797
|
+
checks.push({
|
|
798
|
+
code: "agrenting_url_invalid",
|
|
799
|
+
level: "error",
|
|
800
|
+
message: "agrentingUrl must be an http:// or https:// URL."
|
|
801
|
+
});
|
|
802
|
+
}
|
|
803
|
+
if (!config.apiKey) {
|
|
804
|
+
checks.push({
|
|
805
|
+
code: "agrenting_api_key_missing",
|
|
806
|
+
level: "error",
|
|
807
|
+
message: "Agrenting requires a user API token.",
|
|
808
|
+
hint: "Create an ap_... token in Agrenting and store it as a Paperclip secret."
|
|
809
|
+
});
|
|
810
|
+
}
|
|
811
|
+
if (!config.agentDid) {
|
|
812
|
+
checks.push({
|
|
813
|
+
code: "agrenting_agent_did_missing",
|
|
814
|
+
level: "error",
|
|
815
|
+
message: "Agrenting requires agentDid."
|
|
816
|
+
});
|
|
817
|
+
}
|
|
818
|
+
if (!checks.some((check) => check.level === "error")) {
|
|
819
|
+
const client = new AgrentingClient(config);
|
|
820
|
+
let connectionOk = false;
|
|
821
|
+
try {
|
|
822
|
+
await client.listHirings({ limit: 1 });
|
|
823
|
+
connectionOk = true;
|
|
824
|
+
checks.push({
|
|
825
|
+
code: "agrenting_connection_ok",
|
|
826
|
+
level: "info",
|
|
827
|
+
message: "Connected to the authenticated Agrenting hiring API."
|
|
828
|
+
});
|
|
829
|
+
} catch (error) {
|
|
830
|
+
checks.push({
|
|
831
|
+
code: "agrenting_connection_failed",
|
|
832
|
+
level: "error",
|
|
833
|
+
message: "Could not access the authenticated Agrenting hiring API.",
|
|
834
|
+
detail: error instanceof Error ? error.message : String(error)
|
|
835
|
+
});
|
|
836
|
+
}
|
|
837
|
+
if (connectionOk) {
|
|
838
|
+
try {
|
|
839
|
+
const profile = await client.getAgentProfile(config.agentDid);
|
|
840
|
+
checks.push({
|
|
841
|
+
code: "agrenting_agent_profile_ok",
|
|
842
|
+
level: "info",
|
|
843
|
+
message: `Found Agrenting agent ${profile.name} (${profile.did}).`,
|
|
844
|
+
detail: `${profile.capabilities.length} capabilities; base price ${profile.base_price ?? "not reported"}.`
|
|
845
|
+
});
|
|
846
|
+
} catch (error) {
|
|
847
|
+
checks.push({
|
|
848
|
+
code: "agrenting_agent_profile_failed",
|
|
849
|
+
level: "error",
|
|
850
|
+
message: `Could not load Agrenting agent ${config.agentDid}.`,
|
|
851
|
+
detail: error instanceof Error ? error.message : String(error)
|
|
852
|
+
});
|
|
853
|
+
}
|
|
854
|
+
}
|
|
855
|
+
}
|
|
856
|
+
return {
|
|
857
|
+
adapterType: ctx.adapterType,
|
|
858
|
+
status: summarizeChecks(checks),
|
|
859
|
+
checks,
|
|
860
|
+
testedAt: (/* @__PURE__ */ new Date()).toISOString()
|
|
861
|
+
};
|
|
862
|
+
}
|
|
863
|
+
function getPaperclipConfigSchema() {
|
|
864
|
+
return {
|
|
865
|
+
fields: [
|
|
866
|
+
{
|
|
867
|
+
key: "agrentingUrl",
|
|
868
|
+
label: "Agrenting URL",
|
|
869
|
+
type: "text",
|
|
870
|
+
required: true,
|
|
871
|
+
default: "https://agrenting.com",
|
|
872
|
+
hint: "Base URL of the Agrenting platform."
|
|
873
|
+
},
|
|
874
|
+
{
|
|
875
|
+
key: "apiKey",
|
|
876
|
+
label: "API token",
|
|
877
|
+
type: "text",
|
|
878
|
+
required: true,
|
|
879
|
+
hint: "Agrenting user API token (ap_...). Stored as a Paperclip secret.",
|
|
880
|
+
meta: { secret: true }
|
|
881
|
+
},
|
|
882
|
+
{
|
|
883
|
+
key: "agentDid",
|
|
884
|
+
label: "Agent DID",
|
|
885
|
+
type: "text",
|
|
886
|
+
required: true,
|
|
887
|
+
hint: "Marketplace agent DID, for example did:agrenting:code-reviewer."
|
|
888
|
+
},
|
|
889
|
+
{
|
|
890
|
+
key: "capabilityRequested",
|
|
891
|
+
label: "Capability",
|
|
892
|
+
type: "text",
|
|
893
|
+
hint: "Optional default. Falls back to the first capability on the agent profile."
|
|
894
|
+
},
|
|
895
|
+
{
|
|
896
|
+
key: "price",
|
|
897
|
+
label: "Price per hiring (USD)",
|
|
898
|
+
type: "text",
|
|
899
|
+
hint: "Optional. Falls back to the agent's current base price."
|
|
900
|
+
},
|
|
901
|
+
{
|
|
902
|
+
key: "timeoutSec",
|
|
903
|
+
label: "Timeout seconds",
|
|
904
|
+
type: "number",
|
|
905
|
+
default: DEFAULT_TIMEOUT_SEC
|
|
906
|
+
},
|
|
907
|
+
{
|
|
908
|
+
key: "pollIntervalMs",
|
|
909
|
+
label: "Poll interval milliseconds",
|
|
910
|
+
type: "number",
|
|
911
|
+
default: DEFAULT_POLL_INTERVAL_MS
|
|
912
|
+
},
|
|
913
|
+
{
|
|
914
|
+
key: "repoUrl",
|
|
915
|
+
label: "Repository URL",
|
|
916
|
+
type: "text",
|
|
917
|
+
hint: "Optional push target. Push delivery uses a GitHub token already stored in Agrenting."
|
|
918
|
+
},
|
|
919
|
+
{
|
|
920
|
+
key: "deliveryMode",
|
|
921
|
+
label: "Delivery mode",
|
|
922
|
+
type: "select",
|
|
923
|
+
default: "output",
|
|
924
|
+
options: [
|
|
925
|
+
{ value: "output", label: "Return output" },
|
|
926
|
+
{ value: "push", label: "Push to repository" }
|
|
927
|
+
],
|
|
928
|
+
hint: "Use output unless the hiring also has repository credentials."
|
|
929
|
+
}
|
|
930
|
+
]
|
|
931
|
+
};
|
|
932
|
+
}
|
|
933
|
+
var paperclipSessionCodec = {
|
|
934
|
+
deserialize(raw) {
|
|
935
|
+
if (typeof raw === "string") {
|
|
936
|
+
try {
|
|
937
|
+
return asRecord(JSON.parse(raw));
|
|
938
|
+
} catch {
|
|
939
|
+
return null;
|
|
940
|
+
}
|
|
941
|
+
}
|
|
942
|
+
return asRecord(raw);
|
|
943
|
+
},
|
|
944
|
+
serialize(params) {
|
|
945
|
+
return params;
|
|
946
|
+
},
|
|
947
|
+
getDisplayId(params) {
|
|
948
|
+
return nonEmpty(params?.hiringId);
|
|
949
|
+
},
|
|
950
|
+
encode(state) {
|
|
951
|
+
return JSON.stringify(state ?? null);
|
|
952
|
+
},
|
|
953
|
+
decode(blob) {
|
|
954
|
+
if (!blob) return null;
|
|
955
|
+
try {
|
|
956
|
+
return JSON.parse(blob);
|
|
957
|
+
} catch {
|
|
958
|
+
return null;
|
|
959
|
+
}
|
|
960
|
+
}
|
|
961
|
+
};
|
|
962
|
+
function createCanonicalServerAdapter() {
|
|
963
|
+
return {
|
|
964
|
+
type,
|
|
965
|
+
execute: executePaperclip,
|
|
966
|
+
testEnvironment: testPaperclipEnvironment,
|
|
967
|
+
sessionCodec: paperclipSessionCodec,
|
|
968
|
+
getConfigSchema: getPaperclipConfigSchema,
|
|
969
|
+
agentConfigurationDoc
|
|
970
|
+
};
|
|
971
|
+
}
|
|
972
|
+
|
|
429
973
|
// server/src/crypto.ts
|
|
430
974
|
async function verifyWebhookSignature(rawBody, signature, secret) {
|
|
431
975
|
const crypto = await import("crypto");
|
|
@@ -910,11 +1454,11 @@ async function startWebhookListener(config) {
|
|
|
910
1454
|
if (resolvedTaskId) {
|
|
911
1455
|
const pending = pendingTasks.get(resolvedTaskId);
|
|
912
1456
|
if (pending && !pending.settled) {
|
|
913
|
-
const
|
|
914
|
-
pending.status =
|
|
1457
|
+
const status2 = payload.status ?? pending.status;
|
|
1458
|
+
pending.status = status2;
|
|
915
1459
|
pending.progressPercent = payload.progress_percent ?? pending.progressPercent;
|
|
916
1460
|
pending.progressMessage = payload.progress_message ?? pending.progressMessage;
|
|
917
|
-
if (
|
|
1461
|
+
if (status2 === "completed") {
|
|
918
1462
|
pending.settled = true;
|
|
919
1463
|
pending.resolve({
|
|
920
1464
|
success: true,
|
|
@@ -922,7 +1466,7 @@ async function startWebhookListener(config) {
|
|
|
922
1466
|
taskId: resolvedTaskId,
|
|
923
1467
|
durationMs: Date.now() - pending.startedAt
|
|
924
1468
|
});
|
|
925
|
-
} else if (
|
|
1469
|
+
} else if (status2 === "failed") {
|
|
926
1470
|
pending.settled = true;
|
|
927
1471
|
pending.resolve({
|
|
928
1472
|
success: false,
|
|
@@ -930,7 +1474,7 @@ async function startWebhookListener(config) {
|
|
|
930
1474
|
taskId: resolvedTaskId,
|
|
931
1475
|
durationMs: Date.now() - pending.startedAt
|
|
932
1476
|
});
|
|
933
|
-
} else if (
|
|
1477
|
+
} else if (status2 === "cancelled") {
|
|
934
1478
|
pending.settled = true;
|
|
935
1479
|
pending.resolve({
|
|
936
1480
|
success: false,
|
|
@@ -1161,9 +1705,9 @@ async function cancelTask(config, taskId) {
|
|
|
1161
1705
|
var TASK_MAX_RETRIES = 2;
|
|
1162
1706
|
var TASK_RETRY_BASE_DELAY_MS = 1e3;
|
|
1163
1707
|
var TASK_RETRY_MAX_DELAY_MS = 3e4;
|
|
1164
|
-
async function hireAgent(config, agentDid) {
|
|
1708
|
+
async function hireAgent(config, agentDid, options) {
|
|
1165
1709
|
const client = new AgrentingClient(config);
|
|
1166
|
-
return client.hireAgent(agentDid);
|
|
1710
|
+
return client.hireAgent(agentDid, options);
|
|
1167
1711
|
}
|
|
1168
1712
|
async function getAgentProfile(config, agentDid) {
|
|
1169
1713
|
const client = new AgrentingClient(config);
|
|
@@ -1205,6 +1749,10 @@ async function listHirings(config, options) {
|
|
|
1205
1749
|
const client = new AgrentingClient(config);
|
|
1206
1750
|
return client.listHirings(options);
|
|
1207
1751
|
}
|
|
1752
|
+
async function cancelHiring(config, hiringId) {
|
|
1753
|
+
const client = new AgrentingClient(config);
|
|
1754
|
+
return client.cancelHiring(hiringId);
|
|
1755
|
+
}
|
|
1208
1756
|
async function autoSelectAgent(config, options) {
|
|
1209
1757
|
const client = new AgrentingClient(config);
|
|
1210
1758
|
const capabilities = await client.listCapabilities();
|
|
@@ -1230,7 +1778,8 @@ async function autoSelectAgent(config, options) {
|
|
|
1230
1778
|
const minRep = options.minReputation;
|
|
1231
1779
|
filtered = filtered.filter((a) => {
|
|
1232
1780
|
if (!a.reputation_score) return false;
|
|
1233
|
-
|
|
1781
|
+
const reputation = Number(a.reputation_score);
|
|
1782
|
+
return Number.isFinite(reputation) && reputation >= minRep;
|
|
1234
1783
|
});
|
|
1235
1784
|
}
|
|
1236
1785
|
if (filtered.length === 0) {
|
|
@@ -1241,15 +1790,17 @@ async function autoSelectAgent(config, options) {
|
|
|
1241
1790
|
const sortBy = options.sortBy ?? "reputation_score";
|
|
1242
1791
|
if (options.preferAvailable ?? true) {
|
|
1243
1792
|
filtered.sort((a, b) => {
|
|
1244
|
-
const aAvail = a.availability_status === "available" ? 0 : 1;
|
|
1245
|
-
const bAvail = b.availability_status === "available" ? 0 : 1;
|
|
1793
|
+
const aAvail = (a.availability_status ?? a.availability ?? a.status) === "available" ? 0 : 1;
|
|
1794
|
+
const bAvail = (b.availability_status ?? b.availability ?? b.status) === "available" ? 0 : 1;
|
|
1246
1795
|
if (aAvail !== bAvail) return aAvail - bAvail;
|
|
1247
1796
|
return 0;
|
|
1248
1797
|
});
|
|
1249
1798
|
}
|
|
1250
1799
|
filtered.sort((a, b) => {
|
|
1251
1800
|
if (sortBy === "reputation_score") {
|
|
1252
|
-
|
|
1801
|
+
const aReputation = Number(a.reputation_score ?? 0);
|
|
1802
|
+
const bReputation = Number(b.reputation_score ?? 0);
|
|
1803
|
+
return (Number.isFinite(bReputation) ? bReputation : 0) - (Number.isFinite(aReputation) ? aReputation : 0);
|
|
1253
1804
|
}
|
|
1254
1805
|
if (sortBy === "base_price") {
|
|
1255
1806
|
const aPrice = parseFloat(a.base_price ?? "999999");
|
|
@@ -1257,14 +1808,19 @@ async function autoSelectAgent(config, options) {
|
|
|
1257
1808
|
return aPrice - bPrice;
|
|
1258
1809
|
}
|
|
1259
1810
|
if (sortBy === "availability") {
|
|
1260
|
-
const aAvail = a.availability_status === "available" ? 0 : 1;
|
|
1261
|
-
const bAvail = b.availability_status === "available" ? 0 : 1;
|
|
1811
|
+
const aAvail = (a.availability_status ?? a.availability ?? a.status) === "available" ? 0 : 1;
|
|
1812
|
+
const bAvail = (b.availability_status ?? b.availability ?? b.status) === "available" ? 0 : 1;
|
|
1262
1813
|
return aAvail - bAvail;
|
|
1263
1814
|
}
|
|
1264
1815
|
return 0;
|
|
1265
1816
|
});
|
|
1266
1817
|
const selectedAgent = filtered[0];
|
|
1267
|
-
const hireResult = await client.hireAgent(selectedAgent.did
|
|
1818
|
+
const hireResult = await client.hireAgent(selectedAgent.did, {
|
|
1819
|
+
taskDescription: options.taskDescription,
|
|
1820
|
+
capabilityRequested: options.capability,
|
|
1821
|
+
price: selectedAgent.base_price ?? options.maxPrice ?? "0",
|
|
1822
|
+
deliveryMode: "output"
|
|
1823
|
+
});
|
|
1268
1824
|
return {
|
|
1269
1825
|
...hireResult,
|
|
1270
1826
|
selectedAgent
|
|
@@ -1331,12 +1887,63 @@ function processIncomingMessage2(message) {
|
|
|
1331
1887
|
const senderName = message.sender_name ?? "Agent";
|
|
1332
1888
|
return formatAgentResponse(senderName, message.content);
|
|
1333
1889
|
}
|
|
1890
|
+
async function detectModel(config) {
|
|
1891
|
+
if (!config.agentDid) return null;
|
|
1892
|
+
try {
|
|
1893
|
+
const profile = await getAgentProfile(config, config.agentDid);
|
|
1894
|
+
const provider = profile.ai_provider ?? null;
|
|
1895
|
+
const model = profile.ai_model ?? null;
|
|
1896
|
+
if (provider && model) return { provider, model };
|
|
1897
|
+
} catch {
|
|
1898
|
+
}
|
|
1899
|
+
return null;
|
|
1900
|
+
}
|
|
1901
|
+
async function listSkills(config) {
|
|
1902
|
+
const profile = await getAgentProfile(config, config.agentDid);
|
|
1903
|
+
const capabilities = profile.capabilities ?? [];
|
|
1904
|
+
return capabilities.map((cap) => ({
|
|
1905
|
+
id: `${config.agentDid}:${cap}`,
|
|
1906
|
+
name: cap,
|
|
1907
|
+
description: `Capability "${cap}" of agent ${config.agentDid}`
|
|
1908
|
+
}));
|
|
1909
|
+
}
|
|
1910
|
+
async function syncSkills(config, existing) {
|
|
1911
|
+
const remote = await listSkills(config);
|
|
1912
|
+
const remoteIds = new Set(remote.map((s) => s.id));
|
|
1913
|
+
const existingIds = new Set(existing.map((s) => s.id));
|
|
1914
|
+
return {
|
|
1915
|
+
added: remote.filter((s) => !existingIds.has(s.id)),
|
|
1916
|
+
removed: existing.filter((s) => !remoteIds.has(s.id))
|
|
1917
|
+
};
|
|
1918
|
+
}
|
|
1919
|
+
var sessionCodec = paperclipSessionCodec;
|
|
1920
|
+
async function invoke(config, params) {
|
|
1921
|
+
return execute(config, params);
|
|
1922
|
+
}
|
|
1923
|
+
async function status(config, taskId) {
|
|
1924
|
+
const progress = await getTaskProgress(config, taskId);
|
|
1925
|
+
return {
|
|
1926
|
+
status: progress.status,
|
|
1927
|
+
progressPercent: progress.progressPercent,
|
|
1928
|
+
progressMessage: progress.progressMessage
|
|
1929
|
+
};
|
|
1930
|
+
}
|
|
1931
|
+
async function cancel(config, taskId) {
|
|
1932
|
+
const result = await cancelTask(config, taskId);
|
|
1933
|
+
if (!result.success) {
|
|
1934
|
+
throw new Error(result.error ?? "Failed to cancel task");
|
|
1935
|
+
}
|
|
1936
|
+
}
|
|
1334
1937
|
function createServerAdapter() {
|
|
1938
|
+
const canonical = createCanonicalServerAdapter();
|
|
1335
1939
|
return {
|
|
1940
|
+
...canonical,
|
|
1336
1941
|
name: "agrenting",
|
|
1337
|
-
|
|
1338
|
-
|
|
1339
|
-
|
|
1942
|
+
// Legacy helpers remain available under explicit names. The canonical
|
|
1943
|
+
// Paperclip keys above use AdapterExecutionContext and structured tests.
|
|
1944
|
+
legacyExecute: execute,
|
|
1945
|
+
legacyTestEnvironment: testEnvironment,
|
|
1946
|
+
getLegacyConfigSchema: getConfigSchema,
|
|
1340
1947
|
startWebhookListener,
|
|
1341
1948
|
stopWebhookListener,
|
|
1342
1949
|
registerWebhook,
|
|
@@ -1354,6 +1961,7 @@ function createServerAdapter() {
|
|
|
1354
1961
|
sendMessageToHiring,
|
|
1355
1962
|
getHiringMessages,
|
|
1356
1963
|
retryHiring,
|
|
1964
|
+
cancelHiring,
|
|
1357
1965
|
getHiring,
|
|
1358
1966
|
listHirings,
|
|
1359
1967
|
autoSelectAgent,
|
|
@@ -1363,20 +1971,65 @@ function createServerAdapter() {
|
|
|
1363
1971
|
getBalance,
|
|
1364
1972
|
getTransactions,
|
|
1365
1973
|
deposit,
|
|
1366
|
-
withdraw
|
|
1974
|
+
withdraw,
|
|
1975
|
+
// Legacy pre-0.4 contract additions:
|
|
1976
|
+
invoke,
|
|
1977
|
+
status,
|
|
1978
|
+
cancel,
|
|
1979
|
+
legacyDetectModel: detectModel,
|
|
1980
|
+
legacyListSkills: listSkills,
|
|
1981
|
+
legacySyncSkills: syncSkills
|
|
1367
1982
|
};
|
|
1368
1983
|
}
|
|
1984
|
+
|
|
1985
|
+
// server/src/apps-v2.ts
|
|
1986
|
+
var agrentingAppGalleryEntry = {
|
|
1987
|
+
key: "agrenting",
|
|
1988
|
+
name: "Agrenting",
|
|
1989
|
+
logoUrl: "https://www.google.com/s2/favicons?domain=agrenting.com&sz=64",
|
|
1990
|
+
tagline: "Discover and hire governed remote AI agents.",
|
|
1991
|
+
description: "Give Paperclip agents governed access to discover, hire, monitor, and cancel remote agents in the Agrenting marketplace.",
|
|
1992
|
+
authKind: "api_key",
|
|
1993
|
+
transportTemplate: {
|
|
1994
|
+
transport: "remote_http",
|
|
1995
|
+
url: "https://agrenting.com/mcp/hirer"
|
|
1996
|
+
},
|
|
1997
|
+
credentialFields: [
|
|
1998
|
+
{
|
|
1999
|
+
label: "Agrenting API key",
|
|
2000
|
+
configPath: "credentials.authorization",
|
|
2001
|
+
helpUrl: "https://agrenting.com/dashboard/api-keys",
|
|
2002
|
+
required: true,
|
|
2003
|
+
placement: "header",
|
|
2004
|
+
key: "Authorization",
|
|
2005
|
+
prefix: "Bearer "
|
|
2006
|
+
}
|
|
2007
|
+
],
|
|
2008
|
+
recommendedDefaults: {
|
|
2009
|
+
access: "all_agents",
|
|
2010
|
+
askFirstRiskLevels: ["write", "destructive"]
|
|
2011
|
+
},
|
|
2012
|
+
urlPatterns: ["https://agrenting.com/mcp/hirer*"]
|
|
2013
|
+
};
|
|
1369
2014
|
// Annotate the CommonJS export names for ESM import in node:
|
|
1370
2015
|
0 && (module.exports = {
|
|
1371
2016
|
AgrentingClient,
|
|
1372
2017
|
MAX_POLLS,
|
|
1373
2018
|
POLL_INTERVALS_MS,
|
|
2019
|
+
agentConfigurationDoc,
|
|
2020
|
+
agrentingAppGalleryEntry,
|
|
1374
2021
|
autoSelectAgent,
|
|
1375
2022
|
canSubmitTask,
|
|
2023
|
+
cancel,
|
|
2024
|
+
cancelHiring,
|
|
2025
|
+
cancelTask,
|
|
1376
2026
|
checkBalance,
|
|
1377
2027
|
createServerAdapter,
|
|
1378
2028
|
createWebhookHandler,
|
|
1379
2029
|
deregisterWebhook,
|
|
2030
|
+
detectModel,
|
|
2031
|
+
execute,
|
|
2032
|
+
executePaperclip,
|
|
1380
2033
|
executeWithRetry,
|
|
1381
2034
|
formatAgentResponse,
|
|
1382
2035
|
formatInsufficientBalanceComment,
|
|
@@ -1385,13 +2038,20 @@ function createServerAdapter() {
|
|
|
1385
2038
|
getActiveTaskMappings,
|
|
1386
2039
|
getAgentProfile,
|
|
1387
2040
|
getBackoffMs,
|
|
2041
|
+
getConfigSchema,
|
|
1388
2042
|
getHiring,
|
|
1389
2043
|
getHiringMessages,
|
|
2044
|
+
getPaperclipConfigSchema,
|
|
1390
2045
|
getTaskMessages,
|
|
2046
|
+
getTaskProgress,
|
|
1391
2047
|
getWebhookGracePeriodMs,
|
|
1392
2048
|
hireAgent,
|
|
2049
|
+
invoke,
|
|
2050
|
+
label,
|
|
1393
2051
|
listCapabilities,
|
|
1394
2052
|
listHirings,
|
|
2053
|
+
listSkills,
|
|
2054
|
+
paperclipSessionCodec,
|
|
1395
2055
|
pollTaskUntilDone,
|
|
1396
2056
|
processIncomingMessage,
|
|
1397
2057
|
reassignTask,
|
|
@@ -1400,6 +2060,12 @@ function createServerAdapter() {
|
|
|
1400
2060
|
retryHiring,
|
|
1401
2061
|
sendMessageToHiring,
|
|
1402
2062
|
sendMessageToTask,
|
|
2063
|
+
sessionCodec,
|
|
2064
|
+
status,
|
|
2065
|
+
syncSkills,
|
|
2066
|
+
testEnvironment,
|
|
2067
|
+
testPaperclipEnvironment,
|
|
2068
|
+
type,
|
|
1403
2069
|
unregisterTaskMapping,
|
|
1404
2070
|
verifyWebhookSignature
|
|
1405
2071
|
});
|