rentahuman-mcp 3.2.0 → 3.3.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/README.md +28 -0
- package/dist/serve.js +126 -13
- package/dist/types/index.d.ts +3 -1
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -211,9 +211,11 @@ worker (`escrow_recipient_mismatch`).
|
|
|
211
211
|
To target every eligible worker in one country, pass `location` with the ISO
|
|
212
212
|
country code and `isRemoteAllowed: false`, omitting `city` and `state`. The
|
|
213
213
|
same shape works with `update_bounty`. Platform-blocked countries are rejected.
|
|
214
|
+
To open one bounty to several countries, pass `supportedCountries` instead.
|
|
214
215
|
|
|
215
216
|
```text
|
|
216
217
|
location: { country: "US", isRemoteAllowed: false }
|
|
218
|
+
supportedCountries: ["US", "CA", "GB", "NZ", "AU"]
|
|
217
219
|
```
|
|
218
220
|
|
|
219
221
|
For ordinary accepted one-shot bounties, the worker marks the task complete by
|
|
@@ -307,6 +309,32 @@ Only workers whose profile country matches can apply or receive automatic
|
|
|
307
309
|
outreach. The country gate still applies when remote work is allowed. Include a
|
|
308
310
|
`city` for city-level targeting instead.
|
|
309
311
|
|
|
312
|
+
To recruit across several countries, pass `supportedCountries` with the ISO
|
|
313
|
+
codes (country names are recognized too). Omit `location.country`, or keep it
|
|
314
|
+
and make sure it appears in the list:
|
|
315
|
+
|
|
316
|
+
```json
|
|
317
|
+
{
|
|
318
|
+
"location": { "isRemoteAllowed": true },
|
|
319
|
+
"supportedCountries": ["US", "CA", "GB", "NZ", "AU"],
|
|
320
|
+
"identityRequired": true
|
|
321
|
+
}
|
|
322
|
+
```
|
|
323
|
+
|
|
324
|
+
The list is normalized to uppercase ISO codes and deduplicated; blocked or
|
|
325
|
+
unrecognized countries are rejected with a `400`. With `identityRequired`, the
|
|
326
|
+
list is enforced against the applicant's verified document country. Use
|
|
327
|
+
`isRemoteAllowed: true`: an on-site bounty (`isRemoteAllowed: false`) with a
|
|
328
|
+
location country is gated by that country alone and accepts no other list. On
|
|
329
|
+
`update_bounty`, send the full new list to change it. Omitting it leaves an
|
|
330
|
+
explicit multi-country list in place, so editing `location` alone never drops
|
|
331
|
+
it; legacy bounties that only mirror a country-only `location` keep deriving
|
|
332
|
+
that single country from `location`. An empty array is stored as an explicit
|
|
333
|
+
empty list; it never lifts a country-only or on-site country restriction
|
|
334
|
+
(remove `location.country` first), and a remote city location with a country
|
|
335
|
+
stays unrestricted as before. `aiManaged` bounties do not accept
|
|
336
|
+
`supportedCountries`.
|
|
337
|
+
|
|
310
338
|
#### Completion deadline (auto-reassign)
|
|
311
339
|
|
|
312
340
|
The bounty-level `deadline` is the application cutoff. At or after that time,
|
package/dist/serve.js
CHANGED
|
@@ -1326,6 +1326,7 @@ var agentCheckoutTools = [
|
|
|
1326
1326
|
var BOUNTY_PRICE_TYPES = ["fixed", "hourly"];
|
|
1327
1327
|
var BOUNTY_MIN_PRICE_USD = 3;
|
|
1328
1328
|
var MAX_EXCLUDED_PARTICIPANT_SOURCE_BOUNTIES = 1e3;
|
|
1329
|
+
var MAX_BOUNTY_SUPPORTED_COUNTRIES = 250;
|
|
1329
1330
|
var HUMANIZATION_FORMATS = ["text"];
|
|
1330
1331
|
var HUMANIZATION_TRANSFORMATIONS = [
|
|
1331
1332
|
"paraphrase",
|
|
@@ -1480,6 +1481,11 @@ var BountyLocationRequest = Schema5.Struct({
|
|
|
1480
1481
|
isRemoteAllowed: Schema5.optional(Schema5.Boolean)
|
|
1481
1482
|
});
|
|
1482
1483
|
var BOUNTY_LOCATION_DESCRIPTION = "Worker location target. Set country to an ISO country code without city or state to target every eligible worker in that country, including when remote work is allowed. Include city (and optionally state/country) for city-level targeting. Platform-blocked countries are rejected.";
|
|
1484
|
+
var BountySupportedCountriesRequest = Schema5.Array(
|
|
1485
|
+
Schema5.String.pipe(Schema5.minLength(1), Schema5.maxLength(100))
|
|
1486
|
+
).pipe(Schema5.maxItems(MAX_BOUNTY_SUPPORTED_COUNTRIES));
|
|
1487
|
+
var BOUNTY_SUPPORTED_COUNTRIES_CREATE_DESCRIPTION = 'Countries whose workers may see and apply, as ISO country codes (country names are also recognized), e.g. ["US", "CA", "GB", "NZ", "AU"]. Omit it to keep the location-derived policy: a country-only location restricts the bounty to that country, an on-site city location keeps its existing country gate, and other locations stay unrestricted. When location.country is set it must appear in this list, and on-site bounties (isRemoteAllowed false) with a location country accept only that one country; use isRemoteAllowed true for a remote multi-country bounty. Platform-blocked countries and unrecognized values are rejected. With identityRequired, the list is enforced against the verified document country. Not accepted on aiManaged bounties.';
|
|
1488
|
+
var BOUNTY_SUPPORTED_COUNTRIES_UPDATE_DESCRIPTION = 'Replace the eligible-country list with the full new list of ISO country codes (country names are also recognized), e.g. ["US", "CA", "GB", "NZ", "AU"]. Omitting it leaves an explicit multi-country list in place, so a location-only edit never drops it; legacy bounties that only mirror a country-only location keep deriving that single country from location. On-site bounties (isRemoteAllowed false) with a location country accept only that one country. An empty array is stored as an explicit empty list; it never lifts a country-only or on-site country restriction (remove location.country first), while a remote city location with a country stays unrestricted as before. When location.country is set it must appear in the list. Platform-blocked countries and unrecognized values are rejected.';
|
|
1483
1489
|
var LIFECYCLE_MESSAGES_DESCRIPTION = "Auto-sent to the doer in the bounty conversation on lifecycle transitions. Supports {{name}}, {{bountyTitle}}, {{deadline}}, {{reason}} interpolation. Accepted/submission templates are only ever delivered to accepted applicants.";
|
|
1484
1490
|
var LifecycleMessageTemplateSchema = Schema5.String.pipe(
|
|
1485
1491
|
Schema5.maxLength(BOUNTY_LIFECYCLE_TEMPLATE_MAX_LENGTH)
|
|
@@ -1627,6 +1633,11 @@ var CreateBountyRequest = Schema5.Struct({
|
|
|
1627
1633
|
location: Schema5.optional(BountyLocationRequest).annotations({
|
|
1628
1634
|
description: BOUNTY_LOCATION_DESCRIPTION
|
|
1629
1635
|
}),
|
|
1636
|
+
supportedCountries: Schema5.optional(
|
|
1637
|
+
BountySupportedCountriesRequest
|
|
1638
|
+
).annotations({
|
|
1639
|
+
description: BOUNTY_SUPPORTED_COUNTRIES_CREATE_DESCRIPTION
|
|
1640
|
+
}),
|
|
1630
1641
|
deadline: Schema5.optional(Schema5.String).annotations({
|
|
1631
1642
|
description: "Application cutoff (ISO 8601 format). At or after this time, the listing is removed from discovery and no new applications or direct uploads are accepted. Use completionWindowHours for a post-acceptance completion deadline."
|
|
1632
1643
|
}),
|
|
@@ -1892,6 +1903,11 @@ var UpdateBountyRequest = Schema5.Struct({
|
|
|
1892
1903
|
location: Schema5.optional(BountyLocationRequest).annotations({
|
|
1893
1904
|
description: BOUNTY_LOCATION_DESCRIPTION
|
|
1894
1905
|
}),
|
|
1906
|
+
supportedCountries: Schema5.optional(
|
|
1907
|
+
BountySupportedCountriesRequest
|
|
1908
|
+
).annotations({
|
|
1909
|
+
description: BOUNTY_SUPPORTED_COUNTRIES_UPDATE_DESCRIPTION
|
|
1910
|
+
}),
|
|
1895
1911
|
status: Schema5.optional(
|
|
1896
1912
|
Schema5.Literal("open", "in_review", "paused", "closed")
|
|
1897
1913
|
).annotations({
|
|
@@ -2383,7 +2399,7 @@ var CancelBountyRequest = BountyCancellationFeedbackRequest.pipe(
|
|
|
2383
2399
|
);
|
|
2384
2400
|
var createBountySpec = {
|
|
2385
2401
|
name: "create_bounty",
|
|
2386
|
-
description: `Create a one-shot task bounty for humans to apply to. Dry-run preview.fundingTotal is the total funding requirement before any existing wallet balance is applied; show that estimate to the operator before posting. Live pending_deposit responses include checkout_total, the exact hosted charge after wallet balance is applied, next to deposit_url. To target an entire country, pass location with an ISO country code and isRemoteAllowed=false while omitting city and state. Standard application bounties support application detail items via applicationDetails, including text questions, optional or required acknowledgment checkboxes, one-file uploads (allowedFileTypes: image, docx, pdf, txt, video, audio), and one required live_video field whose label is the script applicants record with the in-browser camera. For appearance-based bounties where the worker appears on camera or their presence is part of the deliverable (sign holding, UGC, sponsored posts, on-camera video, promo/GTM appearances), always include one required live_video field with a label asking the applicant to explain in two sentences why they are a good fit. Set liveCaptureRequirement to photo or video when accepted workers must capture new camera evidence after acceptance; neither camera-only workflow is cryptographic liveness verification. Do not ask for passwords, OTP/2FA codes, API keys, private keys, seed phrases, government IDs, bank/card details, exact home addresses, dates of birth, or other sensitive personal information. Blank rows are ignored; uploads are capped at 3 fields, acknowledgments at 5 fields, and live video at 1 field. Direct-review photo, video, or document collection bounties use submissionMode and matching upload settings instead of applicationDetails. lifecycleMessages can define auto-message templates for acceptance, rejection, and submission review transitions. Set completionWindowHours (any whole number of hours from 1 to 720, e.g. 36) to give confirmed workers a completion deadline: overdue seats are automatically released and reopened for other applicants (auto-reassign), workers can request extensions, and you approve/deny/grant them per worker. Pass optional \`idempotencyKey\` to make this safe to retry (a replayed key returns the original result instead of duplicating). Applicants are auto-reviewed by default (autoAccept, default true): deterministic checks always run and an AI review runs only for free-text screening answers \u2014 qualified applicants are accepted, clear mismatches rejected with a reason, uncertain cases left pending for manual review; pass autoAccept: false to review and accept every application yourself; on micCheckRequired bounties, set autoAcceptMinMicScore (1-5) to make the mic score a hard cutoff: applications below it are auto-rejected, at/above proceed toward acceptance, and unscored recordings stay pending. When omitted, the platform default 3.0 applies and lower scores only stay pending for manual review. To keep previous participants out, pass excludedParticipantSourceBountyIds with up to ${MAX_EXCLUDED_PARTICIPANT_SOURCE_BOUNTIES} ids of your OWN earlier bounties: any worker who was ever accepted on one of them cannot see or apply to the new bounty, and you can change the list later with update_bounty. AI-managed, QA, and taste run bounties, and never-funded drafts are rejected as sources. BETA: pass aiManaged: true to have the platform fully manage the bounty \u2014 automated recruiting, vetting, submission review, payment release, and a final report. Fixed price USD only, 1-50 spots. Poll the bounty's managed run or subscribe to run.report_ready for the finished work.`,
|
|
2402
|
+
description: `Create a one-shot task bounty for humans to apply to. Dry-run preview.fundingTotal is the total funding requirement before any existing wallet balance is applied; show that estimate to the operator before posting. Live pending_deposit responses include checkout_total, the exact hosted charge after wallet balance is applied, next to deposit_url. To target an entire country, pass location with an ISO country code and isRemoteAllowed=false while omitting city and state. To open one bounty to several countries, pass supportedCountries with the ISO codes (e.g. ["US", "CA", "GB", "NZ", "AU"]); when location.country is set it must be in that list, on-site bounties (isRemoteAllowed=false) accept only their location country, and with identityRequired the list is enforced against the verified document country. Standard application bounties support application detail items via applicationDetails, including text questions, optional or required acknowledgment checkboxes, one-file uploads (allowedFileTypes: image, docx, pdf, txt, video, audio), and one required live_video field whose label is the script applicants record with the in-browser camera. For appearance-based bounties where the worker appears on camera or their presence is part of the deliverable (sign holding, UGC, sponsored posts, on-camera video, promo/GTM appearances), always include one required live_video field with a label asking the applicant to explain in two sentences why they are a good fit. Set liveCaptureRequirement to photo or video when accepted workers must capture new camera evidence after acceptance; neither camera-only workflow is cryptographic liveness verification. Do not ask for passwords, OTP/2FA codes, API keys, private keys, seed phrases, government IDs, bank/card details, exact home addresses, dates of birth, or other sensitive personal information. Blank rows are ignored; uploads are capped at 3 fields, acknowledgments at 5 fields, and live video at 1 field. Direct-review photo, video, or document collection bounties use submissionMode and matching upload settings instead of applicationDetails. lifecycleMessages can define auto-message templates for acceptance, rejection, and submission review transitions. Set completionWindowHours (any whole number of hours from 1 to 720, e.g. 36) to give confirmed workers a completion deadline: overdue seats are automatically released and reopened for other applicants (auto-reassign), workers can request extensions, and you approve/deny/grant them per worker. Pass optional \`idempotencyKey\` to make this safe to retry (a replayed key returns the original result instead of duplicating). Applicants are auto-reviewed by default (autoAccept, default true): deterministic checks always run and an AI review runs only for free-text screening answers \u2014 qualified applicants are accepted, clear mismatches rejected with a reason, uncertain cases left pending for manual review; pass autoAccept: false to review and accept every application yourself; on micCheckRequired bounties, set autoAcceptMinMicScore (1-5) to make the mic score a hard cutoff: applications below it are auto-rejected, at/above proceed toward acceptance, and unscored recordings stay pending. When omitted, the platform default 3.0 applies and lower scores only stay pending for manual review. To keep previous participants out, pass excludedParticipantSourceBountyIds with up to ${MAX_EXCLUDED_PARTICIPANT_SOURCE_BOUNTIES} ids of your OWN earlier bounties: any worker who was ever accepted on one of them cannot see or apply to the new bounty, and you can change the list later with update_bounty. AI-managed, QA, and taste run bounties, and never-funded drafts are rejected as sources. BETA: pass aiManaged: true to have the platform fully manage the bounty \u2014 automated recruiting, vetting, submission review, payment release, and a final report. Fixed price USD only, 1-50 spots. Poll the bounty's managed run or subscribe to run.report_ready for the finished work.`,
|
|
2387
2403
|
input: CreateBountyRequest
|
|
2388
2404
|
};
|
|
2389
2405
|
var getBountySpec = {
|
|
@@ -2424,7 +2440,7 @@ var decideExtensionSpec = {
|
|
|
2424
2440
|
};
|
|
2425
2441
|
var updateBountySpec = {
|
|
2426
2442
|
name: "update_bounty",
|
|
2427
|
-
description: `Update ordinary one-shot bounty details. You can modify the title, description, price, application-cutoff deadline, location, requiredLinks, applicationDetails, lifecycleMessages, liveCaptureRequirement, reactivate hidden inactive bounties, and more. The deadline stops new applications and direct uploads; completionWindowHours controls post-acceptance completion timing. To target an entire country, pass location with an ISO country code and isRemoteAllowed=false while omitting city and state. Live capture and required-link requirements can only be changed before applications are received. applicationDetails may be changed after applications are received; existing applications keep their original answers, while future applications use the latest fields. applicationDetails are application detail items for standard application bounties only; blank rows are ignored, uploads are capped at 3 fields, acknowledgments at 5 fields, and camera-only live_video at 1 required field. lifecycleMessages can define auto-message templates for acceptance, rejection, and submission review transitions. Admin-only ongoing bounty settings are intentionally not exposed through MCP. You can also pause/unpause a bounty (status 'paused' stops new applications and outreach, including on a fully staffed bounty; status 'open' resumes it, and the resumed status is derived from its seats: open, partially_filled, or assigned when every seat is still filled), close an unassigned bounty and return its unused funding (status 'closed'), increase seats via spotsAvailable, keep pending applicants on fill via keepApplicantsOnFill, and change the auto-reassign completion deadline via completionWindowHours (any whole number of hours from 1 to 720; null disables; only affects seats confirmed after the edit), and toggle automatic applicant review via autoAccept (applications received while off stay pending; toggling on affects future applications only; ignored on aiManaged bounties) or adjust its mic-quality cutoff via autoAcceptMinMicScore (1-5 \u2014 applications scoring below your configured cutoff are auto-rejected, at/above proceed; null resets to the 3.0 default, below which applications only stay pending). The exclude-previous-participants list can be changed after posting: pass excludedParticipantSourceBountyIds with the full new list of up to ${MAX_EXCLUDED_PARTICIPANT_SOURCE_BOUNTIES} ids of your OWN earlier bounties (an empty array clears it); the same source rules as create_bounty apply, and a bounty can never exclude itself. Use cancel_bounty for cancellation and refund handling. Work completion and payment are system-managed from escrow and payout evidence; status 'completed' and 'paid' cannot be set directly.`,
|
|
2443
|
+
description: `Update ordinary one-shot bounty details. You can modify the title, description, price, application-cutoff deadline, location, requiredLinks, applicationDetails, lifecycleMessages, liveCaptureRequirement, reactivate hidden inactive bounties, and more. The deadline stops new applications and direct uploads; completionWindowHours controls post-acceptance completion timing. To target an entire country, pass location with an ISO country code and isRemoteAllowed=false while omitting city and state. To change which countries may apply, pass supportedCountries with the full new list of ISO codes; omitting it leaves an explicit multi-country list in place (legacy bounties that only mirror a country-only location keep deriving that country from location), an empty array is stored as an explicit empty list but never lifts a country-only or on-site country restriction, and on-site bounties accept only their location country. Live capture and required-link requirements can only be changed before applications are received. applicationDetails may be changed after applications are received; existing applications keep their original answers, while future applications use the latest fields. applicationDetails are application detail items for standard application bounties only; blank rows are ignored, uploads are capped at 3 fields, acknowledgments at 5 fields, and camera-only live_video at 1 required field. lifecycleMessages can define auto-message templates for acceptance, rejection, and submission review transitions. Admin-only ongoing bounty settings are intentionally not exposed through MCP. You can also pause/unpause a bounty (status 'paused' stops new applications and outreach, including on a fully staffed bounty; status 'open' resumes it, and the resumed status is derived from its seats: open, partially_filled, or assigned when every seat is still filled), close an unassigned bounty and return its unused funding (status 'closed'), increase seats via spotsAvailable, keep pending applicants on fill via keepApplicantsOnFill, and change the auto-reassign completion deadline via completionWindowHours (any whole number of hours from 1 to 720; null disables; only affects seats confirmed after the edit), and toggle automatic applicant review via autoAccept (applications received while off stay pending; toggling on affects future applications only; ignored on aiManaged bounties) or adjust its mic-quality cutoff via autoAcceptMinMicScore (1-5 \u2014 applications scoring below your configured cutoff are auto-rejected, at/above proceed; null resets to the 3.0 default, below which applications only stay pending). The exclude-previous-participants list can be changed after posting: pass excludedParticipantSourceBountyIds with the full new list of up to ${MAX_EXCLUDED_PARTICIPANT_SOURCE_BOUNTIES} ids of your OWN earlier bounties (an empty array clears it); the same source rules as create_bounty apply, and a bounty can never exclude itself. Use cancel_bounty for cancellation and refund handling. Work completion and payment are system-managed from escrow and payout evidence; status 'completed' and 'paid' cannot be set directly.`,
|
|
2428
2444
|
input: UpdateBountyRequest
|
|
2429
2445
|
};
|
|
2430
2446
|
var cancelBountySpec = {
|
|
@@ -3175,7 +3191,12 @@ var common = {
|
|
|
3175
3191
|
invalidTimeFormat: "Invalid time format. Use HH:mm (e.g., 09:00, 14:30)."
|
|
3176
3192
|
};
|
|
3177
3193
|
var bounties = {
|
|
3178
|
-
dryRunPreview: "This is a preview \u2014 the bounty has NOT been created yet. preview.fundingTotal is the total funding requirement (including the platform fee) before any existing wallet balance is applied, so it may be higher than the hosted checkout charge. Show this estimate to the operator and ask if they'd like to edit anything before posting. To create the bounty, call create_bounty again with the same parameters and dryRun omitted or set to false.",
|
|
3194
|
+
dryRunPreview: "This is a preview \u2014 the bounty has NOT been created yet, no funds were reserved, and no checkout was started. preview.fundingTotal is the total funding requirement (including the platform fee) before any existing wallet balance is applied, so it may be higher than the hosted checkout charge. Show this estimate to the operator and ask if they'd like to edit anything before posting. To create the bounty, call create_bounty again with the same parameters and dryRun omitted or set to false.",
|
|
3195
|
+
dryRunServerValidated: "serverValidation=passed: the RentAHuman API validated and normalized these parameters with the same rules as a real create (country targeting, pricing, screening fields, content moderation). The AI content review runs only on the real create, so a live bounty can additionally land in pending_review.",
|
|
3196
|
+
dryRunServerUnavailable: "serverValidation=unavailable: the RentAHuman API could not be reached, so this preview was computed locally and has NOT been validated by the server (country targeting, pricing rules and screening fields are only checked there). Tell the operator the preview is unverified and retry the preview before creating.",
|
|
3197
|
+
dryRunMockPreview: "serverValidation=skipped: mock mode computes the preview locally without contacting the API.",
|
|
3198
|
+
retryWithSameKey: (idempotencyKey) => `The API did not confirm whether the bounty was created. Retry create_bounty with the same parameters and the same idempotencyKey ("${idempotencyKey}") after about 30 seconds: a replay returns the original bounty (including its deposit_url) instead of creating a duplicate, and a 409 idempotency_in_progress means the first attempt is still running \u2014 wait and retry again. Do not change the idempotencyKey.`,
|
|
3199
|
+
retryWithoutKey: "The API did not confirm whether the bounty was created, and this call had no idempotencyKey, so a plain retry could create a duplicate. First call list_bounties (mine=true) to check for a bounty with this title (an unfunded one is in pending_deposit status and is not publicly visible). If it is missing, retry with an idempotencyKey so further retries are safe.",
|
|
3179
3200
|
createdMultiPerson: (spotsAvailable) => `Multi-person bounty created successfully. Looking for ${spotsAvailable} humans.`,
|
|
3180
3201
|
createdSingle: "Bounty created successfully. Humans can now view and apply.",
|
|
3181
3202
|
depositRequired: "DEPOSIT REQUIRED: The bounty has been created but is NOT visible yet. Tell the operator checkout_total (it includes a platform fee) before they pay. They must complete checkout at the deposit_url to make the bounty live. Share this URL with the human who owns this account.",
|
|
@@ -3300,6 +3321,12 @@ var MOCK_HIRING_STATUSES = /* @__PURE__ */ new Set([
|
|
|
3300
3321
|
"pending_funding",
|
|
3301
3322
|
"partially_filled"
|
|
3302
3323
|
]);
|
|
3324
|
+
var asRecord = (value) => {
|
|
3325
|
+
if (typeof value !== "object" || value === null || Array.isArray(value)) {
|
|
3326
|
+
return {};
|
|
3327
|
+
}
|
|
3328
|
+
return Object.fromEntries(Object.entries(value));
|
|
3329
|
+
};
|
|
3303
3330
|
var mockHiringSpotsRemaining = (bounty) => MOCK_HIRING_STATUSES.has(bounty.status) ? Math.max(0, (bounty.spotsAvailable || 1) - (bounty.spotsFilled || 0)) : 0;
|
|
3304
3331
|
var handleCreateBounty = (args) => Effect9.gen(function* () {
|
|
3305
3332
|
const params = yield* decode(CreateBountyRequest)(args);
|
|
@@ -3317,6 +3344,11 @@ var handleCreateBounty = (args) => Effect9.gen(function* () {
|
|
|
3317
3344
|
const screeningFields = screeningFieldsResult.fields;
|
|
3318
3345
|
const excludedParticipantSourceBountyIds = params.excludedParticipantSourceBountyIds?.length ? params.excludedParticipantSourceBountyIds : void 0;
|
|
3319
3346
|
const exclusionFields = excludedParticipantSourceBountyIds ? { excludedParticipantSourceBountyIds } : {};
|
|
3347
|
+
const {
|
|
3348
|
+
dryRun: _dryRun,
|
|
3349
|
+
excludedParticipantSourceBountyIds: _rawExclusion,
|
|
3350
|
+
...createParams
|
|
3351
|
+
} = params;
|
|
3320
3352
|
if (params.dryRun) {
|
|
3321
3353
|
const spotsAvailable = params.spotsAvailable || 1;
|
|
3322
3354
|
const currency = params.currency || "USD";
|
|
@@ -3329,6 +3361,7 @@ var handleCreateBounty = (args) => Effect9.gen(function* () {
|
|
|
3329
3361
|
liveCaptureRequirement: params.liveCaptureRequirement || null,
|
|
3330
3362
|
category: params.category || "computer-gigs",
|
|
3331
3363
|
location: params.location || { isRemoteAllowed: true },
|
|
3364
|
+
supportedCountries: params.supportedCountries ?? null,
|
|
3332
3365
|
deadline: params.deadline || null,
|
|
3333
3366
|
estimatedHours: params.estimatedHours,
|
|
3334
3367
|
estimatedDurationUnit: params.estimatedDurationUnit || null,
|
|
@@ -3365,11 +3398,66 @@ var handleCreateBounty = (args) => Effect9.gen(function* () {
|
|
|
3365
3398
|
spotsAvailable
|
|
3366
3399
|
})
|
|
3367
3400
|
};
|
|
3401
|
+
if (config.mockMode) {
|
|
3402
|
+
return ok({
|
|
3403
|
+
success: true,
|
|
3404
|
+
dryRun: true,
|
|
3405
|
+
serverValidation: "skipped",
|
|
3406
|
+
preview,
|
|
3407
|
+
message: `${messages.bounties.dryRunPreview} ${messages.bounties.dryRunMockPreview}`
|
|
3408
|
+
});
|
|
3409
|
+
}
|
|
3410
|
+
const verification2 = yield* identity2.createVerification("create_bounty");
|
|
3411
|
+
const { idempotencyKey: _previewKey, ...previewParams } = createParams;
|
|
3412
|
+
const outcome = yield* api.post("/bounties", {
|
|
3413
|
+
...previewParams,
|
|
3414
|
+
...exclusionFields,
|
|
3415
|
+
applicationDetails: screeningFields,
|
|
3416
|
+
dryRun: true,
|
|
3417
|
+
...verification2
|
|
3418
|
+
}).pipe(
|
|
3419
|
+
Effect9.map((result2) => ({
|
|
3420
|
+
kind: "server",
|
|
3421
|
+
result: asRecord(result2)
|
|
3422
|
+
})),
|
|
3423
|
+
Effect9.catchTag(
|
|
3424
|
+
"TimeoutError",
|
|
3425
|
+
() => Effect9.succeed({ kind: "unreachable" })
|
|
3426
|
+
),
|
|
3427
|
+
Effect9.catchTag(
|
|
3428
|
+
"ApiError",
|
|
3429
|
+
(error) => Effect9.succeed(
|
|
3430
|
+
error.statusCode === 0 ? { kind: "unreachable" } : { kind: "rejected", error }
|
|
3431
|
+
)
|
|
3432
|
+
)
|
|
3433
|
+
);
|
|
3434
|
+
if (outcome.kind === "rejected") {
|
|
3435
|
+
return err({
|
|
3436
|
+
success: false,
|
|
3437
|
+
dryRun: true,
|
|
3438
|
+
error: outcome.error.body,
|
|
3439
|
+
errorType: "ApiError",
|
|
3440
|
+
statusCode: outcome.error.statusCode,
|
|
3441
|
+
endpoint: outcome.error.endpoint
|
|
3442
|
+
});
|
|
3443
|
+
}
|
|
3444
|
+
if (outcome.kind === "unreachable") {
|
|
3445
|
+
return ok({
|
|
3446
|
+
success: true,
|
|
3447
|
+
dryRun: true,
|
|
3448
|
+
serverValidation: "unavailable",
|
|
3449
|
+
preview,
|
|
3450
|
+
message: `${messages.bounties.dryRunPreview} ${messages.bounties.dryRunServerUnavailable}`
|
|
3451
|
+
});
|
|
3452
|
+
}
|
|
3453
|
+
const serverPreview = asRecord(outcome.result.preview);
|
|
3454
|
+
const fundingTotal = typeof outcome.result.fundingTotal === "number" ? outcome.result.fundingTotal : preview.fundingTotal;
|
|
3368
3455
|
return ok({
|
|
3369
3456
|
success: true,
|
|
3370
3457
|
dryRun: true,
|
|
3371
|
-
|
|
3372
|
-
|
|
3458
|
+
serverValidation: "passed",
|
|
3459
|
+
preview: { ...serverPreview, fundingTotal },
|
|
3460
|
+
message: `${messages.bounties.dryRunPreview} ${messages.bounties.dryRunServerValidated}`
|
|
3373
3461
|
});
|
|
3374
3462
|
}
|
|
3375
3463
|
yield* rateLimit.check("create_bounty");
|
|
@@ -3387,6 +3475,7 @@ var handleCreateBounty = (args) => Effect9.gen(function* () {
|
|
|
3387
3475
|
skillsNeeded: params.skillsNeeded || [],
|
|
3388
3476
|
category: params.category || "computer-gigs",
|
|
3389
3477
|
location: params.location || { isRemoteAllowed: true },
|
|
3478
|
+
...params.supportedCountries !== void 0 ? { supportedCountries: params.supportedCountries } : {},
|
|
3390
3479
|
deadline: params.deadline,
|
|
3391
3480
|
estimatedHours: params.estimatedHours,
|
|
3392
3481
|
priceType: params.priceType,
|
|
@@ -3428,18 +3517,42 @@ var handleCreateBounty = (args) => Effect9.gen(function* () {
|
|
|
3428
3517
|
})
|
|
3429
3518
|
);
|
|
3430
3519
|
}
|
|
3431
|
-
const {
|
|
3432
|
-
dryRun: _,
|
|
3433
|
-
excludedParticipantSourceBountyIds: _rawExclusion,
|
|
3434
|
-
...createParams
|
|
3435
|
-
} = params;
|
|
3436
3520
|
const verification = yield* identity2.createVerification("create_bounty");
|
|
3437
|
-
const
|
|
3521
|
+
const created = yield* api.post("/bounties", {
|
|
3438
3522
|
...createParams,
|
|
3439
3523
|
...exclusionFields,
|
|
3440
3524
|
applicationDetails: screeningFields,
|
|
3441
3525
|
...verification
|
|
3442
|
-
})
|
|
3526
|
+
}).pipe(
|
|
3527
|
+
Effect9.map((result2) => ({
|
|
3528
|
+
ok: true,
|
|
3529
|
+
result: asRecord(result2)
|
|
3530
|
+
})),
|
|
3531
|
+
// Upstream failures (gateway errors, timeouts, no response) leave the
|
|
3532
|
+
// agent unsure whether the bounty exists. Client errors keep their
|
|
3533
|
+
// normal error path — they are answers, not uncertainty.
|
|
3534
|
+
Effect9.catchTag(
|
|
3535
|
+
"TimeoutError",
|
|
3536
|
+
(error) => Effect9.succeed({ ok: false, error })
|
|
3537
|
+
),
|
|
3538
|
+
Effect9.catchTag(
|
|
3539
|
+
"ApiError",
|
|
3540
|
+
(error) => error.statusCode === 0 || error.statusCode >= 500 ? Effect9.succeed({ ok: false, error }) : Effect9.fail(error)
|
|
3541
|
+
)
|
|
3542
|
+
);
|
|
3543
|
+
if (!created.ok) {
|
|
3544
|
+
const failure = created.error;
|
|
3545
|
+
const isApiError = "statusCode" in failure;
|
|
3546
|
+
return err({
|
|
3547
|
+
success: false,
|
|
3548
|
+
error: failure.message,
|
|
3549
|
+
errorType: isApiError ? "ApiError" : "TimeoutError",
|
|
3550
|
+
...isApiError ? { statusCode: failure.statusCode } : {},
|
|
3551
|
+
endpoint: failure.endpoint,
|
|
3552
|
+
retry_guidance: params.idempotencyKey ? messages.bounties.retryWithSameKey(params.idempotencyKey) : messages.bounties.retryWithoutKey
|
|
3553
|
+
});
|
|
3554
|
+
}
|
|
3555
|
+
const result = created.result;
|
|
3443
3556
|
if (result.deposit_url) {
|
|
3444
3557
|
return ok({
|
|
3445
3558
|
...result,
|
|
@@ -3908,7 +4021,7 @@ var payEnterpriseBountyTool = toMcpTool(
|
|
|
3908
4021
|
var bountyTools = [
|
|
3909
4022
|
{
|
|
3910
4023
|
name: "create_bounty",
|
|
3911
|
-
description: `Create a one-shot task bounty for humans to apply to. To target an entire country, pass location with an ISO country code and isRemoteAllowed=false while omitting city and state. **IMPORTANT: Always call with dryRun=true first** to preview the bounty. Dry-run \`preview.fundingTotal\` is the total funding requirement before any existing wallet balance is applied. Show that estimate to the operator before posting. Show the preview to the user and ask 'Here's your bounty \u2014 would you like to edit anything before posting?' Only call again with dryRun=false (or omitted) after the user confirms. **You MUST help the user define completionCriteria and evidenceTypes** \u2014 ask what 'done' looks like and what proof they need (text/data, photos, video, or links). For specialized standard-application tasks, configure applicationDetails so applicants provide application details before review. Blank rows are ignored; application upload fields are one file each and capped at 3 total; acknowledgment checkboxes are capped at 5 total and can be optional or required; and one required live_video field may contain a script applicants must record with the in-browser camera. For appearance-based bounties where the worker appears on camera or their presence is part of the deliverable (sign holding, UGC, sponsored posts, on-camera video, promo/GTM appearances), always include one required live_video field with a label asking the applicant to explain in two sentences why they are a good fit \u2014 this gates applications on a live self-recorded video so the poster sees each applicant before accepting. Do not ask for passwords, OTP/2FA codes, API keys, private keys, seed phrases, government IDs, bank/card details, exact home addresses, dates of birth, or other sensitive personal information. For direct-review collection bounties, use submissionMode='photo_upload', 'video_upload', or 'document_upload' with matching submission settings instead of applicationDetails. lifecycleMessages can define auto-message templates for acceptance, rejection, and submission review transitions. You can require applicants to provide specific links (LinkedIn, GitHub, resume, etc.) using the requiredLinks parameter. Requires RENTAHUMAN_API_KEY from the account owner. Standard accounts use available wallet balance first; if the wallet cannot cover the bounty, the response includes deposit_url and checkout_total (the hosted charge after wallet balance is applied) and the account owner must complete checkout before the bounty is visible. Supports multi-person bounties by setting spotsAvailable > 1. Set completionWindowHours (any whole number of hours from 1 to 720, e.g. 36) to give confirmed workers a completion deadline: overdue seats auto-release and reopen for other applicants; workers may request extensions you answer with decide_extension_request. Pass optional \`idempotencyKey\` to make this safe to retry (a replayed key returns the original result instead of duplicating). Applicants are auto-reviewed by default (autoAccept, default true): deterministic checks always run and an AI review runs only for free-text screening answers \u2014 qualified applicants are accepted, clear mismatches rejected with a reason, uncertain cases left pending for manual review; pass autoAccept: false to review and accept every application yourself; on micCheckRequired bounties, set autoAcceptMinMicScore (1-5) to make the mic score a hard cutoff: applications below it are auto-rejected, at/above proceed toward acceptance, and unscored recordings stay pending. When omitted, the platform default 3.0 applies and lower scores only stay pending for manual review. To keep previous participants out, pass excludedParticipantSourceBountyIds with up to ${MAX_EXCLUDED_PARTICIPANT_SOURCE_BOUNTIES} ids of your OWN earlier bounties: any worker who was ever accepted on one of them cannot see or apply to the new bounty, and you can change the list later with update_bounty. AI-managed, QA, and taste run bounties, and never-funded drafts are rejected as sources. BETA: pass aiManaged: true to have the platform fully manage the bounty \u2014 automated recruiting, vetting, submission review, payment release, and a final report. Fixed price USD only, 1-50 spots. Poll the bounty's managed run or subscribe to run.report_ready for the finished work.`,
|
|
4024
|
+
description: `Create a one-shot task bounty for humans to apply to. To target an entire country, pass location with an ISO country code and isRemoteAllowed=false while omitting city and state. **IMPORTANT: Always call with dryRun=true first** to preview the bounty. A preview never creates a bounty, reserves funds, or starts a checkout. When the API is reachable it validates and normalizes the parameters with the same rules as a real create and the response carries serverValidation=passed; if the API cannot be reached the preview is computed locally and carries serverValidation=unavailable \u2014 tell the operator it is unverified. Dry-run \`preview.fundingTotal\` is the total funding requirement before any existing wallet balance is applied. Show that estimate to the operator before posting. Show the preview to the user and ask 'Here's your bounty \u2014 would you like to edit anything before posting?' Only call again with dryRun=false (or omitted) after the user confirms. **You MUST help the user define completionCriteria and evidenceTypes** \u2014 ask what 'done' looks like and what proof they need (text/data, photos, video, or links). For specialized standard-application tasks, configure applicationDetails so applicants provide application details before review. Blank rows are ignored; application upload fields are one file each and capped at 3 total; acknowledgment checkboxes are capped at 5 total and can be optional or required; and one required live_video field may contain a script applicants must record with the in-browser camera. For appearance-based bounties where the worker appears on camera or their presence is part of the deliverable (sign holding, UGC, sponsored posts, on-camera video, promo/GTM appearances), always include one required live_video field with a label asking the applicant to explain in two sentences why they are a good fit \u2014 this gates applications on a live self-recorded video so the poster sees each applicant before accepting. Do not ask for passwords, OTP/2FA codes, API keys, private keys, seed phrases, government IDs, bank/card details, exact home addresses, dates of birth, or other sensitive personal information. For direct-review collection bounties, use submissionMode='photo_upload', 'video_upload', or 'document_upload' with matching submission settings instead of applicationDetails. lifecycleMessages can define auto-message templates for acceptance, rejection, and submission review transitions. You can require applicants to provide specific links (LinkedIn, GitHub, resume, etc.) using the requiredLinks parameter. Requires RENTAHUMAN_API_KEY from the account owner. Standard accounts use available wallet balance first; if the wallet cannot cover the bounty, the response includes deposit_url and checkout_total (the hosted charge after wallet balance is applied) and the account owner must complete checkout before the bounty is visible. Supports multi-person bounties by setting spotsAvailable > 1. Set completionWindowHours (any whole number of hours from 1 to 720, e.g. 36) to give confirmed workers a completion deadline: overdue seats auto-release and reopen for other applicants; workers may request extensions you answer with decide_extension_request. Pass optional \`idempotencyKey\` to make this safe to retry (a replayed key returns the original result instead of duplicating). Applicants are auto-reviewed by default (autoAccept, default true): deterministic checks always run and an AI review runs only for free-text screening answers \u2014 qualified applicants are accepted, clear mismatches rejected with a reason, uncertain cases left pending for manual review; pass autoAccept: false to review and accept every application yourself; on micCheckRequired bounties, set autoAcceptMinMicScore (1-5) to make the mic score a hard cutoff: applications below it are auto-rejected, at/above proceed toward acceptance, and unscored recordings stay pending. When omitted, the platform default 3.0 applies and lower scores only stay pending for manual review. To keep previous participants out, pass excludedParticipantSourceBountyIds with up to ${MAX_EXCLUDED_PARTICIPANT_SOURCE_BOUNTIES} ids of your OWN earlier bounties: any worker who was ever accepted on one of them cannot see or apply to the new bounty, and you can change the list later with update_bounty. AI-managed, QA, and taste run bounties, and never-funded drafts are rejected as sources. BETA: pass aiManaged: true to have the platform fully manage the bounty \u2014 automated recruiting, vetting, submission review, payment release, and a final report. Fixed price USD only, 1-50 spots. Poll the bounty's managed run or subscribe to run.report_ready for the finished work.`,
|
|
3912
4025
|
inputSchema: toInputSchema(CreateBountyRequest),
|
|
3913
4026
|
handler: handleCreateBounty
|
|
3914
4027
|
},
|
package/dist/types/index.d.ts
CHANGED
|
@@ -49,7 +49,7 @@ declare const CreateBountyRequest: Schema.Struct<{
|
|
|
49
49
|
title: Schema.refine<string, Schema.filter<typeof Schema.String>>;
|
|
50
50
|
description: Schema.refine<string, Schema.filter<typeof Schema.String>>;
|
|
51
51
|
completionCriteria: Schema.refine<string, Schema.filter<typeof Schema.String>>;
|
|
52
|
-
evidenceTypes: Schema.refine<readonly ("
|
|
52
|
+
evidenceTypes: Schema.refine<readonly ("text" | "link" | "photo" | "video")[], Schema.Array$<Schema.Literal<["text", "photo", "video", "link"]>>>;
|
|
53
53
|
evidenceCriteria: Schema.optional<Schema.filter<typeof Schema.String>>;
|
|
54
54
|
liveCaptureRequirement: Schema.optional<Schema.Literal<["photo", "video"]>>;
|
|
55
55
|
requirements: Schema.optional<Schema.Array$<typeof Schema.String>>;
|
|
@@ -61,6 +61,7 @@ declare const CreateBountyRequest: Schema.Struct<{
|
|
|
61
61
|
country: Schema.optional<Schema.filter<typeof Schema.String>>;
|
|
62
62
|
isRemoteAllowed: Schema.optional<typeof Schema.Boolean>;
|
|
63
63
|
}>>;
|
|
64
|
+
supportedCountries: Schema.optional<Schema.filter<Schema.Array$<Schema.filter<Schema.filter<typeof Schema.String>>>>>;
|
|
64
65
|
deadline: Schema.optional<typeof Schema.String>;
|
|
65
66
|
estimatedHours: Schema.refine<number, Schema.filter<typeof Schema.Number>>;
|
|
66
67
|
priceType: Schema.Literal<["fixed", "hourly"]>;
|
|
@@ -145,6 +146,7 @@ declare const UpdateBountyRequest: Schema.Struct<{
|
|
|
145
146
|
country: Schema.optional<Schema.filter<typeof Schema.String>>;
|
|
146
147
|
isRemoteAllowed: Schema.optional<typeof Schema.Boolean>;
|
|
147
148
|
}>>;
|
|
149
|
+
supportedCountries: Schema.optional<Schema.filter<Schema.Array$<Schema.filter<Schema.filter<typeof Schema.String>>>>>;
|
|
148
150
|
status: Schema.optional<Schema.Literal<["open", "in_review", "paused", "closed"]>>;
|
|
149
151
|
identityRequired: Schema.optional<typeof Schema.Boolean>;
|
|
150
152
|
excludedParticipantSourceBountyIds: Schema.optional<Schema.filter<Schema.Array$<typeof Schema.String>>>;
|