@butlerbot/sdk 0.0.38 → 0.0.40
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/dist/config.d.ts +1 -1
- package/dist/config.js +1 -1
- package/dist/index.d.ts +6 -4
- package/dist/index.js +7 -2
- package/dist/modules/jobs.d.ts +21 -2
- package/dist/modules/jobs.js +15 -2
- package/dist/types/jobs/job.d.ts +33 -0
- package/dist/types/outreach/delivery.d.ts +16 -1
- package/dist/types/outreach/delivery.js +4 -3
- package/package.json +1 -1
- package/readme.md +14 -2
package/dist/config.d.ts
CHANGED
|
@@ -60,7 +60,7 @@ export declare const CONFIG: {
|
|
|
60
60
|
};
|
|
61
61
|
};
|
|
62
62
|
jobs: {
|
|
63
|
-
/** The collection. One job is `${base}/${jobId}`, its cancel `${base}/${jobId}/cancel`. */
|
|
63
|
+
/** The collection. One job is `${base}/${jobId}`, its cancel `${base}/${jobId}/cancel` and its resume `${base}/${jobId}/resume`. */
|
|
64
64
|
base: string;
|
|
65
65
|
/** The owner's job settings, which live on the user rather than on a job. */
|
|
66
66
|
settings: string;
|
package/dist/config.js
CHANGED
|
@@ -52,7 +52,7 @@ exports.CONFIG = {
|
|
|
52
52
|
}
|
|
53
53
|
},
|
|
54
54
|
jobs: {
|
|
55
|
-
/** The collection. One job is `${base}/${jobId}`, its cancel `${base}/${jobId}/cancel`. */
|
|
55
|
+
/** The collection. One job is `${base}/${jobId}`, its cancel `${base}/${jobId}/cancel` and its resume `${base}/${jobId}/resume`. */
|
|
56
56
|
base: "/api/jobs",
|
|
57
57
|
/** The owner's job settings, which live on the user rather than on a job. */
|
|
58
58
|
settings: "/api/user/jobs-settings",
|
package/dist/index.d.ts
CHANGED
|
@@ -2,7 +2,7 @@ import { APIPath } from "./config";
|
|
|
2
2
|
import { Link, LinkOptions } from "./link";
|
|
3
3
|
import { Conversation, ConversationOptions } from "./modules/conversation";
|
|
4
4
|
import { UsagePolicyDataOptions } from "./modules/usage";
|
|
5
|
-
import { type CancelJobOptions, type GetJobJournalOptions, type GetJobOptions, type SetPhaseModelOptions, type ListJobsOptions, type UpdateJobOptions, type UpdateJobSettingsOptions } from "./modules/jobs";
|
|
5
|
+
import { type CancelJobOptions, type GetJobJournalOptions, type GetJobOptions, type SetPhaseModelOptions, type ListJobsOptions, type ResumeJobOptions, type UpdateJobOptions, type UpdateJobSettingsOptions } from "./modules/jobs";
|
|
6
6
|
import { type AnswerDeliveryOptions, type ListDeliveriesOptions } from "./modules/outreach";
|
|
7
7
|
type OptionalApiKey<T> = Omit<T, "apiKey"> & {
|
|
8
8
|
/** Optional API key, defaults to API key specified in client */
|
|
@@ -52,7 +52,9 @@ export declare class ButlerBotClient {
|
|
|
52
52
|
getJobJournal(config: OptionalApiKey<GetJobJournalOptions>): Promise<import("./types/type_registry").JobJournalPage>;
|
|
53
53
|
/** Stops a job. Throws a `ButlerBotAPIError` with `isConflict` when it had already finished */
|
|
54
54
|
cancelJob(config: OptionalApiKey<CancelJobOptions>): Promise<import("./types/type_registry").JobCancelResponse>;
|
|
55
|
-
/**
|
|
55
|
+
/** Continues a job parked waiting for budget. Throws a `ButlerBotAPIError` with `isConflict` when it is in any other status */
|
|
56
|
+
resumeJob(config: OptionalApiKey<ResumeJobOptions>): Promise<import("./types/type_registry").JobResumeResponse>;
|
|
57
|
+
/** Changes one job's name, the model it runs on, its spending cap and its autonomy: how far it may act, until when, and what it always asks about */
|
|
56
58
|
updateJob(config: OptionalApiKey<UpdateJobOptions>): Promise<import("./types/type_registry").JobUpdateResponse>;
|
|
57
59
|
/** Changes the model one phase of a job's plan runs on; only a phase that has not started */
|
|
58
60
|
setPhaseModel(config: OptionalApiKey<SetPhaseModelOptions>): Promise<import("./types/type_registry").JobPhaseModelResponse>;
|
|
@@ -72,8 +74,8 @@ export type { ConversationOptions };
|
|
|
72
74
|
export type { APIPath };
|
|
73
75
|
export type { ConversationStream, ConversationTransport, TransportTurnRequest, TransportHandlers, } from "./modules/transport";
|
|
74
76
|
export { ButlerBotAPIError } from "./util/api_error";
|
|
75
|
-
export { listJobs, getJob, cancelJob, updateJob, updateJobSettings } from "./modules/jobs";
|
|
76
|
-
export type { JobsRequestOptions, ListJobsOptions, GetJobOptions, CancelJobOptions, UpdateJobOptions, UpdateJobSettingsOptions, } from "./modules/jobs";
|
|
77
|
+
export { listJobs, getJob, cancelJob, resumeJob, updateJob, updateJobSettings } from "./modules/jobs";
|
|
78
|
+
export type { JobsRequestOptions, ListJobsOptions, GetJobOptions, CancelJobOptions, ResumeJobOptions, UpdateJobOptions, UpdateJobSettingsOptions, } from "./modules/jobs";
|
|
77
79
|
export { listDeliveries, answerDelivery } from "./modules/outreach";
|
|
78
80
|
export type { OutreachRequestOptions, ListDeliveriesOptions, AnswerDeliveryOptions, } from "./modules/outreach";
|
|
79
81
|
export { LinkConversationTransport } from "./modules/transport_link";
|
package/dist/index.js
CHANGED
|
@@ -14,7 +14,7 @@ var __exportStar = (this && this.__exportStar) || function(m, exports) {
|
|
|
14
14
|
for (var p in m) if (p !== "default" && !Object.prototype.hasOwnProperty.call(exports, p)) __createBinding(exports, m, p);
|
|
15
15
|
};
|
|
16
16
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
17
|
-
exports.SSEConversationTransport = exports.LinkConversationTransport = exports.answerDelivery = exports.listDeliveries = exports.updateJobSettings = exports.updateJob = exports.cancelJob = exports.getJob = exports.listJobs = exports.ButlerBotAPIError = exports.Conversation = exports.ButlerBotClient = void 0;
|
|
17
|
+
exports.SSEConversationTransport = exports.LinkConversationTransport = exports.answerDelivery = exports.listDeliveries = exports.updateJobSettings = exports.updateJob = exports.resumeJob = exports.cancelJob = exports.getJob = exports.listJobs = exports.ButlerBotAPIError = exports.Conversation = exports.ButlerBotClient = void 0;
|
|
18
18
|
const config_1 = require("./config");
|
|
19
19
|
const link_1 = require("./link");
|
|
20
20
|
const conversation_1 = require("./modules/conversation");
|
|
@@ -88,7 +88,11 @@ class ButlerBotClient {
|
|
|
88
88
|
cancelJob(config) {
|
|
89
89
|
return (0, jobs_1.cancelJob)(this.forRequest(config));
|
|
90
90
|
}
|
|
91
|
-
/**
|
|
91
|
+
/** Continues a job parked waiting for budget. Throws a `ButlerBotAPIError` with `isConflict` when it is in any other status */
|
|
92
|
+
resumeJob(config) {
|
|
93
|
+
return (0, jobs_1.resumeJob)(this.forRequest(config));
|
|
94
|
+
}
|
|
95
|
+
/** Changes one job's name, the model it runs on, its spending cap and its autonomy: how far it may act, until when, and what it always asks about */
|
|
92
96
|
updateJob(config) {
|
|
93
97
|
return (0, jobs_1.updateJob)(this.forRequest(config));
|
|
94
98
|
}
|
|
@@ -123,6 +127,7 @@ var jobs_2 = require("./modules/jobs");
|
|
|
123
127
|
Object.defineProperty(exports, "listJobs", { enumerable: true, get: function () { return jobs_2.listJobs; } });
|
|
124
128
|
Object.defineProperty(exports, "getJob", { enumerable: true, get: function () { return jobs_2.getJob; } });
|
|
125
129
|
Object.defineProperty(exports, "cancelJob", { enumerable: true, get: function () { return jobs_2.cancelJob; } });
|
|
130
|
+
Object.defineProperty(exports, "resumeJob", { enumerable: true, get: function () { return jobs_2.resumeJob; } });
|
|
126
131
|
Object.defineProperty(exports, "updateJob", { enumerable: true, get: function () { return jobs_2.updateJob; } });
|
|
127
132
|
Object.defineProperty(exports, "updateJobSettings", { enumerable: true, get: function () { return jobs_2.updateJobSettings; } });
|
|
128
133
|
var outreach_2 = require("./modules/outreach");
|
package/dist/modules/jobs.d.ts
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import type { JobAutonomy, JobCancelResponse, JobDetailResponse, JobJournalPage, JobListResponse, JobPhaseModelResponse, JobSettingsResponse, JobSettingsUpdate, JobStatus, JobUpdateResponse } from "../types/jobs";
|
|
1
|
+
import type { JobAutonomy, JobCancelResponse, JobDetailResponse, JobJournalPage, JobListResponse, JobPhaseModelResponse, JobResumeResponse, JobSettingsResponse, JobSettingsUpdate, JobStatus, JobUpdateResponse } from "../types/jobs";
|
|
2
2
|
/** What every jobs call needs: where the server is, and who is asking. */
|
|
3
3
|
export type JobsRequestOptions = {
|
|
4
4
|
serverURL?: string;
|
|
@@ -28,6 +28,9 @@ export type GetJobJournalOptions = JobsRequestOptions & {
|
|
|
28
28
|
export type CancelJobOptions = JobsRequestOptions & {
|
|
29
29
|
jobId: string;
|
|
30
30
|
};
|
|
31
|
+
export type ResumeJobOptions = JobsRequestOptions & {
|
|
32
|
+
jobId: string;
|
|
33
|
+
};
|
|
31
34
|
export type UpdateJobOptions = JobsRequestOptions & {
|
|
32
35
|
jobId: string;
|
|
33
36
|
/** A new short name for the job, a few words at most. */
|
|
@@ -40,6 +43,13 @@ export type UpdateJobOptions = JobsRequestOptions & {
|
|
|
40
43
|
alwaysAsk?: string[];
|
|
41
44
|
/** A ceiling on what the job may spend in all, in dollars. Null clears it. */
|
|
42
45
|
capUsd?: number | null;
|
|
46
|
+
/**
|
|
47
|
+
* The model or alias the whole job runs on, and the plan's default.
|
|
48
|
+
*
|
|
49
|
+
* A name sets it; null clears it, leaving the planner to pick a model per phase. One
|
|
50
|
+
* phase's model on its own is `setPhaseModel`.
|
|
51
|
+
*/
|
|
52
|
+
model?: string | null;
|
|
43
53
|
};
|
|
44
54
|
export type SetPhaseModelOptions = JobsRequestOptions & {
|
|
45
55
|
jobId: string;
|
|
@@ -62,7 +72,16 @@ export declare function getJobJournal(options: GetJobJournalOptions): Promise<Jo
|
|
|
62
72
|
* `isConflict` is true and whose `body` still carries the job as it stands.
|
|
63
73
|
*/
|
|
64
74
|
export declare function cancelJob(options: CancelJobOptions): Promise<JobCancelResponse>;
|
|
65
|
-
/**
|
|
75
|
+
/**
|
|
76
|
+
* Continues a job that is parked waiting for budget, when there is room for it again.
|
|
77
|
+
*
|
|
78
|
+
* The answer says what happened either way: `resumed` is the job queued again, and otherwise
|
|
79
|
+
* `reason`, `action` and `message` say why it is still parked and what would change that. A
|
|
80
|
+
* job in any other status is a 409: the call throws a `ButlerBotAPIError` whose `isConflict`
|
|
81
|
+
* is true and whose `body` still carries the job as it stands.
|
|
82
|
+
*/
|
|
83
|
+
export declare function resumeJob(options: ResumeJobOptions): Promise<JobResumeResponse>;
|
|
84
|
+
/** Changes one job's name, the model it runs on, its spending cap and its autonomy: how far it may act, until when, and what it always asks about. */
|
|
66
85
|
export declare function updateJob(options: UpdateJobOptions): Promise<JobUpdateResponse>;
|
|
67
86
|
/**
|
|
68
87
|
* Changes the model one phase of a job's plan runs on.
|
package/dist/modules/jobs.js
CHANGED
|
@@ -4,6 +4,7 @@ exports.listJobs = listJobs;
|
|
|
4
4
|
exports.getJob = getJob;
|
|
5
5
|
exports.getJobJournal = getJobJournal;
|
|
6
6
|
exports.cancelJob = cancelJob;
|
|
7
|
+
exports.resumeJob = resumeJob;
|
|
7
8
|
exports.updateJob = updateJob;
|
|
8
9
|
exports.setPhaseModel = setPhaseModel;
|
|
9
10
|
exports.updateJobSettings = updateJobSettings;
|
|
@@ -51,13 +52,25 @@ async function cancelJob(options) {
|
|
|
51
52
|
const url = (0, url_formatter_1.formatURL)(`${jobsBase(options)}/${encodeURIComponent(options.jobId)}/cancel`, {}, { apiKey: options.apiKey, debug: options.debug });
|
|
52
53
|
return (0, api_request_1.requestAPI)({ url, method: "POST", action: `cancel job ${options.jobId}` });
|
|
53
54
|
}
|
|
54
|
-
/**
|
|
55
|
+
/**
|
|
56
|
+
* Continues a job that is parked waiting for budget, when there is room for it again.
|
|
57
|
+
*
|
|
58
|
+
* The answer says what happened either way: `resumed` is the job queued again, and otherwise
|
|
59
|
+
* `reason`, `action` and `message` say why it is still parked and what would change that. A
|
|
60
|
+
* job in any other status is a 409: the call throws a `ButlerBotAPIError` whose `isConflict`
|
|
61
|
+
* is true and whose `body` still carries the job as it stands.
|
|
62
|
+
*/
|
|
63
|
+
async function resumeJob(options) {
|
|
64
|
+
const url = (0, url_formatter_1.formatURL)(`${jobsBase(options)}/${encodeURIComponent(options.jobId)}/resume`, {}, { apiKey: options.apiKey, debug: options.debug });
|
|
65
|
+
return (0, api_request_1.requestAPI)({ url, method: "POST", action: `resume job ${options.jobId}` });
|
|
66
|
+
}
|
|
67
|
+
/** Changes one job's name, the model it runs on, its spending cap and its autonomy: how far it may act, until when, and what it always asks about. */
|
|
55
68
|
async function updateJob(options) {
|
|
56
69
|
const url = (0, url_formatter_1.formatURL)(`${jobsBase(options)}/${encodeURIComponent(options.jobId)}`, {}, { apiKey: options.apiKey, debug: options.debug });
|
|
57
70
|
return (0, api_request_1.requestAPI)({
|
|
58
71
|
url,
|
|
59
72
|
method: "PATCH",
|
|
60
|
-
body: body({ title: options.title, autonomy: options.autonomy, autonomyUntil: options.autonomyUntil, alwaysAsk: options.alwaysAsk, capUsd: options.capUsd }),
|
|
73
|
+
body: body({ title: options.title, autonomy: options.autonomy, autonomyUntil: options.autonomyUntil, alwaysAsk: options.alwaysAsk, capUsd: options.capUsd, model: options.model }),
|
|
61
74
|
action: `update job ${options.jobId}`,
|
|
62
75
|
});
|
|
63
76
|
}
|
package/dist/types/jobs/job.d.ts
CHANGED
|
@@ -52,6 +52,13 @@ export type JobView = {
|
|
|
52
52
|
journalCardUri: string | null;
|
|
53
53
|
/** The chat the job was asked for in, and where it reports back. */
|
|
54
54
|
originConversationId: string | null;
|
|
55
|
+
/**
|
|
56
|
+
* The model or alias the user asked the whole job to run on, and the plan's default.
|
|
57
|
+
*
|
|
58
|
+
* Null when nobody chose one, in which case the planner picks a model per phase. A phase's
|
|
59
|
+
* own `model` is what that phase actually runs on either way.
|
|
60
|
+
*/
|
|
61
|
+
model: string | null;
|
|
55
62
|
/** What was chosen for this job, or null when nothing was. */
|
|
56
63
|
autonomy: JobAutonomy | null;
|
|
57
64
|
/** When `autonomy` lapses back to asking, in UTC milliseconds. */
|
|
@@ -220,3 +227,29 @@ export type JobSettingsResponse = {
|
|
|
220
227
|
success: true;
|
|
221
228
|
jobs: JobSettings;
|
|
222
229
|
};
|
|
230
|
+
/**
|
|
231
|
+
* Why a resume answered as it did.
|
|
232
|
+
*
|
|
233
|
+
* `resumed` is the job queued again; the rest are why it was left where it is — the owner's
|
|
234
|
+
* tier does not allow the job to run, today's allowance is spent, or the server refused for a
|
|
235
|
+
* reason of its own.
|
|
236
|
+
*/
|
|
237
|
+
export type JobResumeReason = "resumed" | "over_tier_limit" | "allowance_spent" | "refused";
|
|
238
|
+
/** What the user can do about a job that was not resumed. */
|
|
239
|
+
export type JobResumeAction = "raise_allowance" | "upgrade_tier" | "wait";
|
|
240
|
+
/**
|
|
241
|
+
* A job after a resume was asked for.
|
|
242
|
+
*
|
|
243
|
+
* `resumed` says whether it is queued again; when it is not, `reason` and `action` say why and
|
|
244
|
+
* what would change it, and `wakeAt` is the earliest the runner would pick it up on its own,
|
|
245
|
+
* in epoch milliseconds. `message` is the same thing in a sentence or two, ready to show.
|
|
246
|
+
*/
|
|
247
|
+
export type JobResumeResponse = {
|
|
248
|
+
success: true;
|
|
249
|
+
resumed: boolean;
|
|
250
|
+
reason: JobResumeReason;
|
|
251
|
+
action?: JobResumeAction;
|
|
252
|
+
wakeAt?: number;
|
|
253
|
+
message: string;
|
|
254
|
+
job: JobView;
|
|
255
|
+
};
|
|
@@ -31,6 +31,14 @@ export type DeliverySurface = {
|
|
|
31
31
|
export declare const DELIVERY_ANSWER_VIA: readonly ["web", "discord", "chat", "tool"];
|
|
32
32
|
/** Which surface the answer came back on. */
|
|
33
33
|
export type DeliveryAnsweredVia = typeof DELIVERY_ANSWER_VIA[number];
|
|
34
|
+
export declare const DELIVERY_CLOSED_REASONS: readonly ["job_done", "job_failed", "job_cancelled"];
|
|
35
|
+
/** Why a delivery was closed with nobody having answered it: what became of the job that asked. */
|
|
36
|
+
export type DeliveryClosedReason = typeof DELIVERY_CLOSED_REASONS[number];
|
|
37
|
+
/** A delivery closed unanswered, and when. */
|
|
38
|
+
export type DeliveryClosed = {
|
|
39
|
+
at: number;
|
|
40
|
+
reason: DeliveryClosedReason;
|
|
41
|
+
};
|
|
34
42
|
export type DeliveryAnswer = {
|
|
35
43
|
at: number;
|
|
36
44
|
text: string;
|
|
@@ -54,9 +62,16 @@ export type Delivery = {
|
|
|
54
62
|
surfaces: DeliverySurface[];
|
|
55
63
|
/** Null until somebody answers. Only ever set once. */
|
|
56
64
|
answered: DeliveryAnswer | null;
|
|
65
|
+
/**
|
|
66
|
+
* Set when the job this belonged to ended before anyone answered, and null otherwise.
|
|
67
|
+
*
|
|
68
|
+
* A closed delivery is no longer open, and answering it is refused: the server answers a
|
|
69
|
+
* 409 the same way it does one already answered.
|
|
70
|
+
*/
|
|
71
|
+
closed: DeliveryClosed | null;
|
|
57
72
|
created: number;
|
|
58
73
|
};
|
|
59
|
-
/** Whether a delivery is still waiting on the user. */
|
|
74
|
+
/** Whether a delivery is still waiting on the user: unanswered, and not closed under it. */
|
|
60
75
|
export declare function isDeliveryOpen(delivery: Delivery): boolean;
|
|
61
76
|
export type DeliveryListResponse = {
|
|
62
77
|
success: true;
|
|
@@ -7,7 +7,7 @@
|
|
|
7
7
|
* on that job's inbox.
|
|
8
8
|
*/
|
|
9
9
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
10
|
-
exports.DELIVERY_ANSWER_VIA = exports.DELIVERY_SURFACE_STATUSES = exports.DELIVERY_SURFACES = exports.ANSWERABLE_DELIVERY_INTENTS = exports.DELIVERY_INTENTS = void 0;
|
|
10
|
+
exports.DELIVERY_CLOSED_REASONS = exports.DELIVERY_ANSWER_VIA = exports.DELIVERY_SURFACE_STATUSES = exports.DELIVERY_SURFACES = exports.ANSWERABLE_DELIVERY_INTENTS = exports.DELIVERY_INTENTS = void 0;
|
|
11
11
|
exports.isDeliveryOpen = isDeliveryOpen;
|
|
12
12
|
exports.DELIVERY_INTENTS = ["question", "update", "approval", "statement"];
|
|
13
13
|
/** The intents that leave something outstanding until the user answers. */
|
|
@@ -15,7 +15,8 @@ exports.ANSWERABLE_DELIVERY_INTENTS = ["question", "approval"];
|
|
|
15
15
|
exports.DELIVERY_SURFACES = ["web", "discord"];
|
|
16
16
|
exports.DELIVERY_SURFACE_STATUSES = ["pending", "sent", "failed"];
|
|
17
17
|
exports.DELIVERY_ANSWER_VIA = ["web", "discord", "chat", "tool"];
|
|
18
|
-
|
|
18
|
+
exports.DELIVERY_CLOSED_REASONS = ["job_done", "job_failed", "job_cancelled"];
|
|
19
|
+
/** Whether a delivery is still waiting on the user: unanswered, and not closed under it. */
|
|
19
20
|
function isDeliveryOpen(delivery) {
|
|
20
|
-
return exports.ANSWERABLE_DELIVERY_INTENTS.includes(delivery.intent) && !delivery.answered;
|
|
21
|
+
return exports.ANSWERABLE_DELIVERY_INTENTS.includes(delivery.intent) && !delivery.answered && !delivery.closed;
|
|
21
22
|
}
|
package/package.json
CHANGED
package/readme.md
CHANGED
|
@@ -335,14 +335,22 @@ const { entries, hasMore } = await client.getJobJournal({ jobId, page: 2 });
|
|
|
335
335
|
await client.updateJob({ jobId, title: "Flat hunt", autonomy: "free", autonomyUntil: Date.now() + 86_400_000, capUsd: 5 });
|
|
336
336
|
await client.setPhaseModel({ jobId, phaseId: "search", model: "Butler-Auto-Smart" });
|
|
337
337
|
await client.cancelJob({ jobId });
|
|
338
|
+
const { resumed, reason, action, message } = await client.resumeJob({ jobId });
|
|
338
339
|
```
|
|
339
340
|
|
|
340
341
|
`getJob` carries the newest page of the journal and `journalTotal`; `getJobJournal` pages the rest,
|
|
341
342
|
newest page first, with each page in the order its entries were written and `hasMore` saying whether
|
|
342
343
|
an older page exists. Each entry says which shift wrote it (`shiftIndex`, `shiftKind`) and on which
|
|
343
344
|
model. A job's `title` is a few words of its own, set at start or renamed with `updateJob`, which
|
|
344
|
-
also takes `capUsd`, a ceiling on what the job may spend in all (null clears it)
|
|
345
|
-
|
|
345
|
+
also takes `capUsd`, a ceiling on what the job may spend in all (null clears it), and `model`, the
|
|
346
|
+
model or alias the whole job runs on and the plan's default — a name sets it, null clears it and
|
|
347
|
+
leaves the planner to pick per phase, and a job's own `model` reads back the same way.
|
|
348
|
+
`setPhaseModel` changes the model one phase that has not started runs on, to one of the detail's
|
|
349
|
+
`phaseModels`.
|
|
350
|
+
`resumeJob` continues a job parked as `waiting_budget` when there is room for it again: it answers
|
|
351
|
+
with `resumed`, a `reason` of `resumed`, `over_tier_limit`, `allowance_spent` or `refused`, an
|
|
352
|
+
`action` of `raise_allowance`, `upgrade_tier` or `wait` when there is one, a `wakeAt` when the job
|
|
353
|
+
would carry on by itself, and a `message` written for the user. A job in any other status is a 409.
|
|
346
354
|
|
|
347
355
|
Paging is 1-based, and every listing answers with `page`, `limit` and `total`. A listing also
|
|
348
356
|
carries `allowance` — what this user's jobs may spend today and what is left of it, or
|
|
@@ -373,6 +381,10 @@ Answering closes the delivery, and when it belongs to a job the answer is put on
|
|
|
373
381
|
inbox, which wakes a job that was parked waiting for it — `job.delivered` says what became of
|
|
374
382
|
it. An approval takes `decision: "approve" | "deny"` alongside the text.
|
|
375
383
|
|
|
384
|
+
A delivery whose job ended before anyone answered is closed instead: `closed` carries the time
|
|
385
|
+
and a reason of `job_done`, `job_failed` or `job_cancelled`. `isDeliveryOpen` reads false for
|
|
386
|
+
one, and answering it is refused with a 409 just as one already answered is.
|
|
387
|
+
|
|
376
388
|
### When a call fails
|
|
377
389
|
|
|
378
390
|
Every jobs and outreach call throws `ButlerBotAPIError` when the server does not answer with a
|