@doist/automations-mcp 1.11.0 → 1.13.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.
|
@@ -9418,6 +9418,11 @@ var automationListResponseSchema = z11.object({
|
|
|
9418
9418
|
data: z11.array(automationSchema),
|
|
9419
9419
|
nextCursor: z11.string().nullable()
|
|
9420
9420
|
});
|
|
9421
|
+
var triggerAutomationResponseSchema = z11.object({
|
|
9422
|
+
automationId: z11.string(),
|
|
9423
|
+
runId: z11.string(),
|
|
9424
|
+
status: z11.literal("pending")
|
|
9425
|
+
});
|
|
9421
9426
|
var updateAutomationSchema = z11.object({
|
|
9422
9427
|
enabled: z11.boolean().optional(),
|
|
9423
9428
|
manifest: manifestSchema.optional()
|
|
@@ -10204,7 +10209,8 @@ var FRONTEND_COHORT_EVENTS = [
|
|
|
10204
10209
|
"template:used",
|
|
10205
10210
|
"top-up:completed",
|
|
10206
10211
|
"guided-creation:started",
|
|
10207
|
-
"guided-creation:completed"
|
|
10212
|
+
"guided-creation:completed",
|
|
10213
|
+
"workspace:switched"
|
|
10208
10214
|
];
|
|
10209
10215
|
var BACKEND_COHORT_EVENTS = [
|
|
10210
10216
|
// API-process (publish lifecycle)
|
|
@@ -11202,13 +11208,19 @@ var AutomationsRequestError = class extends Error {
|
|
|
11202
11208
|
* scope: a challenge can say the grant falls short without saying of what.
|
|
11203
11209
|
*/
|
|
11204
11210
|
requiredScope;
|
|
11205
|
-
|
|
11211
|
+
/**
|
|
11212
|
+
* The `X-Request-Id` the response carried, which the backend logs the request
|
|
11213
|
+
* under. Undefined in a browser unless the header is exposed by CORS.
|
|
11214
|
+
*/
|
|
11215
|
+
requestId;
|
|
11216
|
+
constructor(message, httpStatusCode, responseData, authChallenge, requestId) {
|
|
11206
11217
|
super(message);
|
|
11207
11218
|
this.name = "AutomationsRequestError";
|
|
11208
11219
|
this.httpStatusCode = httpStatusCode;
|
|
11209
11220
|
this.responseData = responseData;
|
|
11210
11221
|
this.authChallenge = authChallenge;
|
|
11211
11222
|
this.requiredScope = parseChallengeScope(authChallenge);
|
|
11223
|
+
this.requestId = requestId;
|
|
11212
11224
|
}
|
|
11213
11225
|
isAuthError() {
|
|
11214
11226
|
return this.httpStatusCode === 401 || this.httpStatusCode === 403;
|
|
@@ -11279,7 +11291,7 @@ async function request(args) {
|
|
|
11279
11291
|
const data = await readResponseBody(response);
|
|
11280
11292
|
if (!response.ok) {
|
|
11281
11293
|
const message = typeof data === "object" && data !== null && "message" in data ? String(data.message) : response.statusText || `Request failed with status ${response.status}`;
|
|
11282
|
-
throw new AutomationsRequestError(message, response.status, data, response.headers.get("WWW-Authenticate") ?? void 0);
|
|
11294
|
+
throw new AutomationsRequestError(message, response.status, data, response.headers.get("WWW-Authenticate") ?? void 0, response.headers.get("X-Request-Id") ?? void 0);
|
|
11283
11295
|
}
|
|
11284
11296
|
return data;
|
|
11285
11297
|
}
|
|
@@ -11699,13 +11711,15 @@ var AutomationsClient = class extends BaseClient {
|
|
|
11699
11711
|
});
|
|
11700
11712
|
return createDraftResponseSchema.parse(data);
|
|
11701
11713
|
}
|
|
11714
|
+
/** Starts a run now. Undefined from an API that predates returning the run. */
|
|
11702
11715
|
async trigger(id, options) {
|
|
11703
11716
|
const parsedId = IdSchema.parse(id);
|
|
11704
|
-
await this.request({
|
|
11717
|
+
const data = await this.request({
|
|
11705
11718
|
method: "POST",
|
|
11706
11719
|
path: automationsEndpoints.trigger(parsedId),
|
|
11707
11720
|
options
|
|
11708
11721
|
});
|
|
11722
|
+
return triggerAutomationResponseSchema.optional().parse(data);
|
|
11709
11723
|
}
|
|
11710
11724
|
async test(id, options) {
|
|
11711
11725
|
const parsedId = IdSchema.parse(id);
|
|
@@ -12040,13 +12054,17 @@ var WorkspaceAutomationsClient = class extends BaseClient {
|
|
|
12040
12054
|
});
|
|
12041
12055
|
return automationSchema.parse(data);
|
|
12042
12056
|
}
|
|
12043
|
-
/**
|
|
12057
|
+
/**
|
|
12058
|
+
* Runs the automation now. The run executes as its creator, not as the caller.
|
|
12059
|
+
* Undefined from an API that predates returning the run.
|
|
12060
|
+
*/
|
|
12044
12061
|
async trigger(workspaceId, id, options) {
|
|
12045
|
-
await this.request({
|
|
12062
|
+
const data = await this.request({
|
|
12046
12063
|
method: "POST",
|
|
12047
12064
|
path: workspaceAutomationsEndpoints.trigger(IdSchema.parse(workspaceId), IdSchema.parse(id)),
|
|
12048
12065
|
options
|
|
12049
12066
|
});
|
|
12067
|
+
return triggerAutomationResponseSchema.optional().parse(data);
|
|
12050
12068
|
}
|
|
12051
12069
|
/**
|
|
12052
12070
|
* Deletes the automation. Workspace admins may delete another member's
|
|
@@ -13451,31 +13469,11 @@ function readManifestName(manifest) {
|
|
|
13451
13469
|
return typeof name === "string" && name.trim() ? name : "(unnamed)";
|
|
13452
13470
|
}
|
|
13453
13471
|
|
|
13454
|
-
// ../../packages/sdk/dist/esm/
|
|
13455
|
-
var DRAFT_TEST_OUTCOMES = [
|
|
13456
|
-
"passed",
|
|
13457
|
-
"failed",
|
|
13458
|
-
"skipped",
|
|
13459
|
-
"cancelled",
|
|
13460
|
-
"timed_out"
|
|
13461
|
-
];
|
|
13472
|
+
// ../../packages/sdk/dist/esm/runs/poll-until-closed.js
|
|
13462
13473
|
var DEFAULT_POLL_INTERVALS_MS = { first: 2e3, max: 1e4 };
|
|
13463
13474
|
var STATUS_REQUEST_GRACE_MS = 1e4;
|
|
13464
|
-
var OPEN_RUN_STATUSES = /* @__PURE__ */ new Set([
|
|
13465
|
-
|
|
13466
|
-
"running"
|
|
13467
|
-
]);
|
|
13468
|
-
async function startDraftTest(draftApi, { draftId, request: request2 }) {
|
|
13469
|
-
const started = await draftApi.test(draftId, request2);
|
|
13470
|
-
return {
|
|
13471
|
-
draftId,
|
|
13472
|
-
testRunId: started.testRunId,
|
|
13473
|
-
trigger: started.trigger,
|
|
13474
|
-
connectorCalls: started.connectorCalls,
|
|
13475
|
-
status: started.status
|
|
13476
|
-
};
|
|
13477
|
-
}
|
|
13478
|
-
async function waitForDraftTest(draftApi, { draftId, testRunId }, { timeoutMs, pollIntervalsMs, requestGraceMs, onPoll, signal }) {
|
|
13475
|
+
var OPEN_RUN_STATUSES = /* @__PURE__ */ new Set(["pending", "running"]);
|
|
13476
|
+
async function pollUntilClosed(read, { timeoutMs, pollIntervalsMs, requestGraceMs, onPoll, signal }) {
|
|
13479
13477
|
const intervals = pollIntervalsMs ?? DEFAULT_POLL_INTERVALS_MS;
|
|
13480
13478
|
const deadline = Date.now() + timeoutMs;
|
|
13481
13479
|
let intervalMs = intervals.first;
|
|
@@ -13484,24 +13482,64 @@ async function waitForDraftTest(draftApi, { draftId, testRunId }, { timeoutMs, p
|
|
|
13484
13482
|
let last = null;
|
|
13485
13483
|
while (true) {
|
|
13486
13484
|
try {
|
|
13487
|
-
last = await
|
|
13485
|
+
last = await read(requestSignal);
|
|
13488
13486
|
} catch (error) {
|
|
13489
13487
|
if (signal.aborted || !requestTimeout.aborted)
|
|
13490
13488
|
throw error;
|
|
13491
|
-
return
|
|
13489
|
+
return { timedOut: true, run: last };
|
|
13492
13490
|
}
|
|
13493
13491
|
if (!OPEN_RUN_STATUSES.has(last.status)) {
|
|
13494
|
-
return
|
|
13492
|
+
return { timedOut: false, run: last };
|
|
13495
13493
|
}
|
|
13496
13494
|
onPoll?.(last);
|
|
13497
13495
|
const remainingMs = deadline - Date.now();
|
|
13498
13496
|
if (remainingMs <= 0) {
|
|
13499
|
-
return
|
|
13497
|
+
return { timedOut: true, run: last };
|
|
13500
13498
|
}
|
|
13501
13499
|
await delay(Math.min(intervalMs, remainingMs), signal);
|
|
13502
13500
|
intervalMs = Math.min(intervalMs * 2, intervals.max);
|
|
13503
13501
|
}
|
|
13504
13502
|
}
|
|
13503
|
+
function delay(ms, signal) {
|
|
13504
|
+
return new Promise((resolve, reject) => {
|
|
13505
|
+
if (signal.aborted) {
|
|
13506
|
+
reject(signal.reason);
|
|
13507
|
+
return;
|
|
13508
|
+
}
|
|
13509
|
+
const onAbort = () => {
|
|
13510
|
+
clearTimeout(timer);
|
|
13511
|
+
reject(signal.reason);
|
|
13512
|
+
};
|
|
13513
|
+
const timer = setTimeout(() => {
|
|
13514
|
+
signal.removeEventListener("abort", onAbort);
|
|
13515
|
+
resolve();
|
|
13516
|
+
}, ms);
|
|
13517
|
+
signal.addEventListener("abort", onAbort, { once: true });
|
|
13518
|
+
});
|
|
13519
|
+
}
|
|
13520
|
+
|
|
13521
|
+
// ../../packages/sdk/dist/esm/drafts/draft-test.js
|
|
13522
|
+
var DRAFT_TEST_OUTCOMES = [
|
|
13523
|
+
"passed",
|
|
13524
|
+
"failed",
|
|
13525
|
+
"skipped",
|
|
13526
|
+
"cancelled",
|
|
13527
|
+
"timed_out"
|
|
13528
|
+
];
|
|
13529
|
+
async function startDraftTest(draftApi, { draftId, request: request2 }) {
|
|
13530
|
+
const started = await draftApi.test(draftId, request2);
|
|
13531
|
+
return {
|
|
13532
|
+
draftId,
|
|
13533
|
+
testRunId: started.testRunId,
|
|
13534
|
+
trigger: started.trigger,
|
|
13535
|
+
connectorCalls: started.connectorCalls,
|
|
13536
|
+
status: started.status
|
|
13537
|
+
};
|
|
13538
|
+
}
|
|
13539
|
+
async function waitForDraftTest(draftApi, { draftId, testRunId }, options) {
|
|
13540
|
+
const polled = await pollUntilClosed((requestSignal) => draftApi.getTestRun(draftId, testRunId, void 0, requestSignal), options);
|
|
13541
|
+
return polled.timedOut ? toFinished(polled.run, "timed_out") : toFinished(polled.run, toOutcome(polled.run.status));
|
|
13542
|
+
}
|
|
13505
13543
|
function toDraftTestTriggerSelector(value) {
|
|
13506
13544
|
if (typeof value === "number")
|
|
13507
13545
|
return { index: value };
|
|
@@ -13528,22 +13566,65 @@ function toOutcome(status) {
|
|
|
13528
13566
|
return "failed";
|
|
13529
13567
|
}
|
|
13530
13568
|
}
|
|
13531
|
-
|
|
13532
|
-
|
|
13533
|
-
|
|
13534
|
-
|
|
13535
|
-
|
|
13536
|
-
|
|
13537
|
-
|
|
13538
|
-
|
|
13539
|
-
|
|
13569
|
+
|
|
13570
|
+
// ../../packages/sdk/dist/esm/runs/run-api.js
|
|
13571
|
+
function bindRunApi(api, workspaceId, { signal } = {}) {
|
|
13572
|
+
if (workspaceId === null) {
|
|
13573
|
+
return {
|
|
13574
|
+
workspaceId: null,
|
|
13575
|
+
getAutomation: (automationId) => api.automations.get(automationId, { signal }),
|
|
13576
|
+
trigger: (automationId) => api.automations.trigger(automationId, { signal }),
|
|
13577
|
+
getRun: (automationId, runId, args = {}, requestSignal) => api.automationRuns.get(automationId, runId, args, {
|
|
13578
|
+
signal: requestSignal ?? signal
|
|
13579
|
+
})
|
|
13540
13580
|
};
|
|
13541
|
-
|
|
13542
|
-
|
|
13543
|
-
|
|
13544
|
-
|
|
13545
|
-
|
|
13546
|
-
|
|
13581
|
+
}
|
|
13582
|
+
return {
|
|
13583
|
+
workspaceId,
|
|
13584
|
+
getAutomation: (automationId) => api.workspaceAutomations.get(workspaceId, automationId, { signal }),
|
|
13585
|
+
trigger: (automationId) => api.workspaceAutomations.trigger(workspaceId, automationId, { signal }),
|
|
13586
|
+
getRun: (automationId, runId, args = {}, requestSignal) => api.workspaceAutomationRuns.get(workspaceId, automationId, runId, args, {
|
|
13587
|
+
signal: requestSignal ?? signal
|
|
13588
|
+
})
|
|
13589
|
+
};
|
|
13590
|
+
}
|
|
13591
|
+
|
|
13592
|
+
// ../../packages/sdk/dist/esm/runs/run-wait.js
|
|
13593
|
+
var RUN_OUTCOMES = ["completed", "failed", "skipped", "cancelled", "timed_out"];
|
|
13594
|
+
async function waitForRun(runApi, { automationId, runId }, options) {
|
|
13595
|
+
const polled = await pollUntilClosed((requestSignal) => runApi.getRun(automationId, runId, void 0, requestSignal), options);
|
|
13596
|
+
return polled.timedOut ? toFinished2(polled.run, "timed_out") : toFinished2(polled.run, toOutcome2(polled.run.status));
|
|
13597
|
+
}
|
|
13598
|
+
function toFinished2(run, outcome) {
|
|
13599
|
+
const failedStep = run ? findFailedStep(run) : null;
|
|
13600
|
+
return {
|
|
13601
|
+
outcome,
|
|
13602
|
+
status: run?.status ?? "pending",
|
|
13603
|
+
failedStep,
|
|
13604
|
+
error: failedStep?.error ?? (run ? getRunOutcomeReason(run) : null) ?? (run?.status === "data-expired" ? "Temporal no longer holds this run's history, so its outcome is unknown" : null),
|
|
13605
|
+
result: run?.result ?? null
|
|
13606
|
+
};
|
|
13607
|
+
}
|
|
13608
|
+
function findFailedStep(run) {
|
|
13609
|
+
const step = run.steps.find((candidate) => candidate.status === "failed");
|
|
13610
|
+
return step ? { stepId: step.stepId, description: step.description, error: step.error ?? null } : null;
|
|
13611
|
+
}
|
|
13612
|
+
function toOutcome2(status) {
|
|
13613
|
+
switch (status) {
|
|
13614
|
+
case "completed":
|
|
13615
|
+
case "skipped":
|
|
13616
|
+
case "cancelled":
|
|
13617
|
+
return status;
|
|
13618
|
+
default:
|
|
13619
|
+
return "failed";
|
|
13620
|
+
}
|
|
13621
|
+
}
|
|
13622
|
+
|
|
13623
|
+
// ../../packages/sdk/dist/esm/drafts/conversation-draft.js
|
|
13624
|
+
async function findConversationDraftId(api, { conversationId, workspaceId, signal }) {
|
|
13625
|
+
const args = { limit: MAX_PAGINATION_LIMIT };
|
|
13626
|
+
const page = workspaceId === null ? await api.automationDrafts.list(args, { signal }) : await api.workspaceAutomationDrafts.list(workspaceId, args, { signal });
|
|
13627
|
+
return page.data.find((draft) => draft.conversationId === conversationId)?.id ?? null;
|
|
13547
13628
|
}
|
|
13548
13629
|
|
|
13549
13630
|
// ../../packages/sdk/dist/esm/chat-turn/connection-requirements.js
|
|
@@ -13935,6 +14016,67 @@ function assertSelectionsUnambiguous(requirements, selections) {
|
|
|
13935
14016
|
|
|
13936
14017
|
// ../../packages/sdk/dist/esm/chat-turn/transport.js
|
|
13937
14018
|
import { DefaultChatTransport, readUIMessageStream } from "ai";
|
|
14019
|
+
|
|
14020
|
+
// ../../packages/sdk/dist/esm/chat-turn/errors.js
|
|
14021
|
+
var ChatTurnError = class extends Error {
|
|
14022
|
+
conversationId;
|
|
14023
|
+
requestId;
|
|
14024
|
+
stage;
|
|
14025
|
+
/** The API's error body, when it refused the request with one. */
|
|
14026
|
+
response;
|
|
14027
|
+
constructor({ conversationId, requestId, stage, cause }) {
|
|
14028
|
+
const response = stage === "request" ? parseChatStreamRequestError(cause) : void 0;
|
|
14029
|
+
super(response?.message ?? (cause instanceof Error ? cause.message : String(cause)), {
|
|
14030
|
+
cause
|
|
14031
|
+
});
|
|
14032
|
+
this.name = "ChatTurnError";
|
|
14033
|
+
this.conversationId = conversationId;
|
|
14034
|
+
this.requestId = requestId;
|
|
14035
|
+
this.stage = stage;
|
|
14036
|
+
this.response = response;
|
|
14037
|
+
}
|
|
14038
|
+
get httpStatus() {
|
|
14039
|
+
return this.response?.statusCode;
|
|
14040
|
+
}
|
|
14041
|
+
};
|
|
14042
|
+
function parseChatStreamRequestError(error) {
|
|
14043
|
+
if (!(error instanceof Error) || !error.message.trim().startsWith("{"))
|
|
14044
|
+
return void 0;
|
|
14045
|
+
let body;
|
|
14046
|
+
try {
|
|
14047
|
+
body = JSON.parse(error.message);
|
|
14048
|
+
} catch {
|
|
14049
|
+
return void 0;
|
|
14050
|
+
}
|
|
14051
|
+
const parsed = chatStreamRequestErrorResponseSchema.safeParse(body);
|
|
14052
|
+
return parsed.success ? parsed.data : void 0;
|
|
14053
|
+
}
|
|
14054
|
+
function getChatTurnBillingRefusal(error) {
|
|
14055
|
+
return getChatBillingRefusal(error instanceof ChatTurnError ? error.cause : error);
|
|
14056
|
+
}
|
|
14057
|
+
function readFailedRequest(error) {
|
|
14058
|
+
if (error instanceof ChatTurnError) {
|
|
14059
|
+
return {
|
|
14060
|
+
requestId: error.requestId,
|
|
14061
|
+
...error.httpStatus !== void 0 ? { httpStatus: error.httpStatus } : {}
|
|
14062
|
+
};
|
|
14063
|
+
}
|
|
14064
|
+
if (error instanceof AutomationsRequestError) {
|
|
14065
|
+
return {
|
|
14066
|
+
...error.requestId ? { requestId: error.requestId } : {},
|
|
14067
|
+
httpStatus: error.httpStatusCode
|
|
14068
|
+
};
|
|
14069
|
+
}
|
|
14070
|
+
return {};
|
|
14071
|
+
}
|
|
14072
|
+
function classifyChatTurnFailure(error) {
|
|
14073
|
+
const { httpStatus } = readFailedRequest(error);
|
|
14074
|
+
if (httpStatus !== void 0)
|
|
14075
|
+
return httpStatus < 500 ? "refused" : "broken";
|
|
14076
|
+
return error instanceof ChatTurnError || error instanceof AutomationsNetworkError ? "broken" : "other";
|
|
14077
|
+
}
|
|
14078
|
+
|
|
14079
|
+
// ../../packages/sdk/dist/esm/chat-turn/transport.js
|
|
13938
14080
|
var ChatTurnTransport = class {
|
|
13939
14081
|
transport;
|
|
13940
14082
|
constructor({ token, baseUrl, headers, fetch: fetch2 }) {
|
|
@@ -13961,39 +14103,62 @@ var ChatTurnTransport = class {
|
|
|
13961
14103
|
}
|
|
13962
14104
|
/** Streams one server turn, reporting incremental text and deduplicated tool state changes. */
|
|
13963
14105
|
async streamTurn({ conversationId, workspaceId = null, message, signal, onText, onToolActivity }) {
|
|
13964
|
-
const
|
|
13965
|
-
|
|
13966
|
-
|
|
13967
|
-
|
|
13968
|
-
|
|
13969
|
-
|
|
13970
|
-
|
|
13971
|
-
|
|
14106
|
+
const requestId = globalThis.crypto.randomUUID();
|
|
14107
|
+
const fail = (stage, cause) => signal?.aborted ? cause : new ChatTurnError({ conversationId, requestId, stage, cause });
|
|
14108
|
+
let stream;
|
|
14109
|
+
try {
|
|
14110
|
+
stream = await this.transport.sendMessages({
|
|
14111
|
+
trigger: "submit-message",
|
|
14112
|
+
chatId: conversationId,
|
|
14113
|
+
messageId: void 0,
|
|
14114
|
+
messages: [message],
|
|
14115
|
+
abortSignal: signal,
|
|
14116
|
+
headers: { "X-Request-Id": requestId },
|
|
14117
|
+
body: { conversationId, workspaceId }
|
|
14118
|
+
});
|
|
14119
|
+
} catch (error) {
|
|
14120
|
+
throw fail("request", error);
|
|
14121
|
+
}
|
|
13972
14122
|
let finalMessage;
|
|
13973
14123
|
let emittedText = "";
|
|
13974
14124
|
const emittedToolStates = /* @__PURE__ */ new Set();
|
|
13975
|
-
|
|
13976
|
-
|
|
13977
|
-
|
|
13978
|
-
|
|
13979
|
-
|
|
13980
|
-
|
|
13981
|
-
|
|
13982
|
-
|
|
13983
|
-
|
|
13984
|
-
|
|
13985
|
-
|
|
13986
|
-
|
|
13987
|
-
|
|
13988
|
-
|
|
13989
|
-
|
|
13990
|
-
|
|
13991
|
-
|
|
13992
|
-
|
|
13993
|
-
|
|
13994
|
-
|
|
13995
|
-
|
|
14125
|
+
let callbackError;
|
|
14126
|
+
const report = (callback) => {
|
|
14127
|
+
try {
|
|
14128
|
+
callback();
|
|
14129
|
+
} catch (error) {
|
|
14130
|
+
callbackError = { error };
|
|
14131
|
+
throw error;
|
|
14132
|
+
}
|
|
14133
|
+
};
|
|
14134
|
+
try {
|
|
14135
|
+
for await (const currentMessage of readUIMessageStream({
|
|
14136
|
+
stream,
|
|
14137
|
+
terminateOnError: true
|
|
14138
|
+
})) {
|
|
14139
|
+
finalMessage = currentMessage;
|
|
14140
|
+
const visibleText = getVisibleText(currentMessage);
|
|
14141
|
+
if (visibleText.startsWith(emittedText)) {
|
|
14142
|
+
const delta = visibleText.slice(emittedText.length);
|
|
14143
|
+
if (delta)
|
|
14144
|
+
report(() => onText?.(delta));
|
|
14145
|
+
emittedText = visibleText;
|
|
14146
|
+
}
|
|
14147
|
+
for (const part of currentMessage.parts) {
|
|
14148
|
+
const activity = toToolActivity(part);
|
|
14149
|
+
if (!activity)
|
|
14150
|
+
continue;
|
|
14151
|
+
const key = `${activity.toolCallId}:${activity.state}`;
|
|
14152
|
+
if (emittedToolStates.has(key))
|
|
14153
|
+
continue;
|
|
14154
|
+
emittedToolStates.add(key);
|
|
14155
|
+
report(() => onToolActivity?.(activity));
|
|
14156
|
+
}
|
|
13996
14157
|
}
|
|
14158
|
+
} catch (error) {
|
|
14159
|
+
if (callbackError)
|
|
14160
|
+
throw callbackError.error;
|
|
14161
|
+
throw fail("stream", error);
|
|
13997
14162
|
}
|
|
13998
14163
|
if (!finalMessage) {
|
|
13999
14164
|
throw new Error("The Automations chat returned an empty response.");
|
|
@@ -14018,7 +14183,7 @@ import { McpServer } from "@modelcontextprotocol/server";
|
|
|
14018
14183
|
// package.json
|
|
14019
14184
|
var package_default = {
|
|
14020
14185
|
name: "@doist/automations-mcp",
|
|
14021
|
-
version: "1.
|
|
14186
|
+
version: "1.13.0",
|
|
14022
14187
|
description: "MCP server and importable AI tools for Todoist Automations",
|
|
14023
14188
|
type: "module",
|
|
14024
14189
|
main: "./dist/index.js",
|
|
@@ -14078,7 +14243,7 @@ var package_default = {
|
|
|
14078
14243
|
"@types/node": "24.19.2",
|
|
14079
14244
|
"conventional-changelog-conventionalcommits": "9.3.1",
|
|
14080
14245
|
esbuild: "0.28.2",
|
|
14081
|
-
"gpt-tokenizer": "4.0
|
|
14246
|
+
"gpt-tokenizer": "4.1.0",
|
|
14082
14247
|
"semantic-release": "25.0.9",
|
|
14083
14248
|
tsx: "4.23.15",
|
|
14084
14249
|
typescript: "6.0.3",
|
|
@@ -15143,6 +15308,7 @@ function startProgressTicker(reportProgress, message, intervalMs = PROGRESS_INTE
|
|
|
15143
15308
|
|
|
15144
15309
|
// src/utils/chat-turn.ts
|
|
15145
15310
|
var TEXT_PREVIEW_LENGTH = 120;
|
|
15311
|
+
var DRAFT_LOOKUP_TIMEOUT_MS = 5e3;
|
|
15146
15312
|
async function runTurn({
|
|
15147
15313
|
reportProgress,
|
|
15148
15314
|
progressIntervalMs = PROGRESS_INTERVAL_MS,
|
|
@@ -15169,8 +15335,9 @@ async function runTurn({
|
|
|
15169
15335
|
onToolActivity: ({ tool, state }) => reportProgress(`${tool}: ${state}`)
|
|
15170
15336
|
});
|
|
15171
15337
|
} catch (error) {
|
|
15338
|
+
stopProgress();
|
|
15172
15339
|
throw new Error(
|
|
15173
|
-
`Conversation ${turn.conversationId}: ${describeTurnError(error, turn
|
|
15340
|
+
`Conversation ${turn.conversationId}: ${await describeTurnError(error, turn)}`
|
|
15174
15341
|
);
|
|
15175
15342
|
} finally {
|
|
15176
15343
|
stopProgress();
|
|
@@ -15185,8 +15352,8 @@ function describeConversationError(conversationId) {
|
|
|
15185
15352
|
throw new Error(`Conversation ${conversationId}: ${formatToolError(error)}`);
|
|
15186
15353
|
};
|
|
15187
15354
|
}
|
|
15188
|
-
function describeTurnError(error,
|
|
15189
|
-
if (signal?.aborted) {
|
|
15355
|
+
async function describeTurnError(error, turn) {
|
|
15356
|
+
if (turn.signal?.aborted) {
|
|
15190
15357
|
return "the turn was cancelled before it finished. Whatever the assistant had said is saved; get-conversation shows it and continue-conversation resumes.";
|
|
15191
15358
|
}
|
|
15192
15359
|
if (error instanceof ChatTurnInputError) {
|
|
@@ -15203,11 +15370,28 @@ function describeTurnError(error, signal) {
|
|
|
15203
15370
|
return error.message;
|
|
15204
15371
|
}
|
|
15205
15372
|
}
|
|
15206
|
-
const refusal =
|
|
15373
|
+
const refusal = getChatTurnBillingRefusal(error)?.message;
|
|
15207
15374
|
if (refusal !== void 0) {
|
|
15208
15375
|
return `the turn was refused. ${refusal.endsWith(".") ? refusal : `${refusal}.`} Retrying won't help until the user acts.`;
|
|
15209
15376
|
}
|
|
15210
|
-
|
|
15377
|
+
const { requestId } = readFailedRequest(error);
|
|
15378
|
+
const reason = formatToolError(error).replace(/\.?$/, ".");
|
|
15379
|
+
const failed = `the turn failed${requestId ? ` (request ${requestId})` : ""}. ${reason} The conversation is intact; continue-conversation resumes it.`;
|
|
15380
|
+
const draftId = classifyChatTurnFailure(error) === "broken" ? await findDraft(turn) : null;
|
|
15381
|
+
return draftId ? `${failed} Its draft ${draftId} is saved; get-draft shows it.` : failed;
|
|
15382
|
+
}
|
|
15383
|
+
function findDraft({
|
|
15384
|
+
api,
|
|
15385
|
+
conversationId,
|
|
15386
|
+
workspaceId,
|
|
15387
|
+
signal
|
|
15388
|
+
}) {
|
|
15389
|
+
const deadline = AbortSignal.timeout(DRAFT_LOOKUP_TIMEOUT_MS);
|
|
15390
|
+
return findConversationDraftId(api, {
|
|
15391
|
+
conversationId,
|
|
15392
|
+
workspaceId: workspaceId ?? null,
|
|
15393
|
+
signal: signal ? AbortSignal.any([signal, deadline]) : deadline
|
|
15394
|
+
}).catch(() => null);
|
|
15211
15395
|
}
|
|
15212
15396
|
function describeTurnResult(result) {
|
|
15213
15397
|
const next = describeNextStep(result);
|
|
@@ -16277,6 +16461,37 @@ var startConversation = {
|
|
|
16277
16461
|
|
|
16278
16462
|
// src/tools/test-draft.ts
|
|
16279
16463
|
import { z as z91 } from "zod";
|
|
16464
|
+
|
|
16465
|
+
// src/utils/run-wait.ts
|
|
16466
|
+
async function waitWithProgress(wait, {
|
|
16467
|
+
run,
|
|
16468
|
+
subject,
|
|
16469
|
+
startedStatus,
|
|
16470
|
+
recovery,
|
|
16471
|
+
timeoutMs,
|
|
16472
|
+
signal,
|
|
16473
|
+
reportProgress
|
|
16474
|
+
}) {
|
|
16475
|
+
let state = startedStatus;
|
|
16476
|
+
reportProgress(`${run} started; waiting up to ${timeoutMs / 6e4} min`);
|
|
16477
|
+
const stopProgress = startProgressTicker(
|
|
16478
|
+
reportProgress,
|
|
16479
|
+
(elapsedSeconds) => `${run}: ${state} (${elapsedSeconds}s)`
|
|
16480
|
+
);
|
|
16481
|
+
try {
|
|
16482
|
+
return await wait((polled) => {
|
|
16483
|
+
const step = polled.steps.find((candidate) => candidate.status === "running");
|
|
16484
|
+
state = step ? `running ${step.stepId}` : polled.status;
|
|
16485
|
+
});
|
|
16486
|
+
} catch (error) {
|
|
16487
|
+
const why = signal.aborted ? "the wait was cancelled." : `checking on it failed: ${formatToolError(error).replace(/\.?$/, ".")}`;
|
|
16488
|
+
throw new Error(`${subject} started, but ${why} ${recovery}`);
|
|
16489
|
+
} finally {
|
|
16490
|
+
stopProgress();
|
|
16491
|
+
}
|
|
16492
|
+
}
|
|
16493
|
+
|
|
16494
|
+
// src/tools/test-draft.ts
|
|
16280
16495
|
var DEFAULT_WAIT_MINUTES = 10;
|
|
16281
16496
|
var parameters27 = {
|
|
16282
16497
|
draftId: z91.string().describe("The draft to test, as listed by find-drafts or get-draft."),
|
|
@@ -16333,11 +16548,23 @@ var testDraft = {
|
|
|
16333
16548
|
structuredContent: { ...base, outcome: "started", status: started.status }
|
|
16334
16549
|
};
|
|
16335
16550
|
}
|
|
16336
|
-
const
|
|
16337
|
-
|
|
16338
|
-
|
|
16339
|
-
|
|
16340
|
-
|
|
16551
|
+
const timeoutMs = waitMinutes * 6e4;
|
|
16552
|
+
const finished = await waitWithProgress(
|
|
16553
|
+
(onPoll) => waitForDraftTest(draftApi, started, {
|
|
16554
|
+
timeoutMs,
|
|
16555
|
+
signal,
|
|
16556
|
+
onPoll
|
|
16557
|
+
}),
|
|
16558
|
+
{
|
|
16559
|
+
run: `Test run ${started.testRunId}`,
|
|
16560
|
+
subject: `Test run ${started.testRunId} of draft ${draftId}`,
|
|
16561
|
+
startedStatus: started.status,
|
|
16562
|
+
recovery: "It keeps running; get-test-run shows how it ends, so do not start another test to check on it.",
|
|
16563
|
+
timeoutMs,
|
|
16564
|
+
signal,
|
|
16565
|
+
reportProgress
|
|
16566
|
+
}
|
|
16567
|
+
);
|
|
16341
16568
|
const output = {
|
|
16342
16569
|
...base,
|
|
16343
16570
|
outcome: finished.outcome,
|
|
@@ -16358,36 +16585,6 @@ var testDraft = {
|
|
|
16358
16585
|
};
|
|
16359
16586
|
}
|
|
16360
16587
|
};
|
|
16361
|
-
async function waitWithProgress(draftApi, started, {
|
|
16362
|
-
timeoutMs,
|
|
16363
|
-
signal,
|
|
16364
|
-
reportProgress
|
|
16365
|
-
}) {
|
|
16366
|
-
const run = `Test run ${started.testRunId}`;
|
|
16367
|
-
let state = started.status;
|
|
16368
|
-
reportProgress(`${run} started; waiting up to ${timeoutMs / 6e4} min`);
|
|
16369
|
-
const stopProgress = startProgressTicker(
|
|
16370
|
-
reportProgress,
|
|
16371
|
-
(elapsedSeconds) => `${run}: ${state} (${elapsedSeconds}s)`
|
|
16372
|
-
);
|
|
16373
|
-
try {
|
|
16374
|
-
return await waitForDraftTest(draftApi, started, {
|
|
16375
|
-
timeoutMs,
|
|
16376
|
-
signal,
|
|
16377
|
-
onPoll: (polled) => {
|
|
16378
|
-
const step = polled.steps.find((candidate) => candidate.status === "running");
|
|
16379
|
-
state = step ? `running ${step.stepId}` : polled.status;
|
|
16380
|
-
}
|
|
16381
|
-
});
|
|
16382
|
-
} catch (error) {
|
|
16383
|
-
const why = signal.aborted ? "the wait was cancelled." : `checking on it failed: ${formatToolError(error).replace(/\.?$/, ".")}`;
|
|
16384
|
-
throw new Error(
|
|
16385
|
-
`${run} of draft ${started.draftId} started, but ${why} It keeps running; get-test-run shows how it ends, so do not start another test to check on it.`
|
|
16386
|
-
);
|
|
16387
|
-
} finally {
|
|
16388
|
-
stopProgress();
|
|
16389
|
-
}
|
|
16390
|
-
}
|
|
16391
16588
|
function describeFinished(output, waitMinutes) {
|
|
16392
16589
|
const run = `Test run ${output.testRunId} of draft ${output.draftId}`;
|
|
16393
16590
|
const otherTriggers = output.trigger.count > 1 ? ` It tested trigger ${output.trigger.index} of ${output.trigger.count}; pass trigger to test another.` : "";
|
|
@@ -16410,24 +16607,35 @@ function describeFinished(output, waitMinutes) {
|
|
|
16410
16607
|
|
|
16411
16608
|
// src/tools/trigger-automation.ts
|
|
16412
16609
|
import { z as z92 } from "zod";
|
|
16610
|
+
var DEFAULT_WAIT_MINUTES2 = 10;
|
|
16413
16611
|
var parameters28 = {
|
|
16414
16612
|
automationId: z92.string().describe("The id of the automation, as returned by find-automations."),
|
|
16613
|
+
waitMinutes: z92.number().int().min(0).max(30).optional().describe(
|
|
16614
|
+
`How long to wait for the run to end; 0 returns once it starts. Default ${DEFAULT_WAIT_MINUTES2}.`
|
|
16615
|
+
),
|
|
16415
16616
|
workspaceId: workspaceIdParameter
|
|
16416
16617
|
};
|
|
16417
16618
|
var outputSchema26 = {
|
|
16418
16619
|
automationId: z92.string(),
|
|
16419
16620
|
name: z92.string(),
|
|
16420
|
-
|
|
16621
|
+
runId: z92.string().optional().describe("get-run reads the run with it."),
|
|
16622
|
+
outcome: z92.enum(["started", ...RUN_OUTCOMES]).describe(
|
|
16623
|
+
"started: not waited for. timed_out: still going when the wait ended; it keeps running."
|
|
16624
|
+
),
|
|
16625
|
+
status: runDetailSchema.shape.status.optional(),
|
|
16626
|
+
failedStep: draftTestRunSchema.shape.failedStep,
|
|
16627
|
+
error: z92.string().optional().describe("The failed step's error, else why the run failed or was skipped."),
|
|
16628
|
+
result: runDetailSchema.shape.result
|
|
16421
16629
|
};
|
|
16422
16630
|
var triggerAutomation = {
|
|
16423
16631
|
name: ToolNames.TRIGGER_AUTOMATION,
|
|
16424
|
-
description: "Starts a run of a schedule-triggered automation now, without waiting for its next scheduled time, and
|
|
16632
|
+
description: "Starts a run of a schedule-triggered automation now, without waiting for its next scheduled time, and waits for it to end, reporting progress meanwhile. Runs even when the automation is disabled. Cannot start automations that run on events or API calls. Confirm intent with the user first.",
|
|
16425
16633
|
parameters: parameters28,
|
|
16426
16634
|
outputSchema: outputSchema26,
|
|
16427
16635
|
annotations: { readOnlyHint: false, destructiveHint: false, idempotentHint: false },
|
|
16428
|
-
async execute({ automationId, workspaceId }, { api, signal }) {
|
|
16429
|
-
const
|
|
16430
|
-
const dto =
|
|
16636
|
+
async execute({ automationId, waitMinutes = DEFAULT_WAIT_MINUTES2, workspaceId }, { api, signal, reportProgress }) {
|
|
16637
|
+
const runApi = bindRunApi(api, workspaceId ?? null, { signal });
|
|
16638
|
+
const dto = await runApi.getAutomation(automationId);
|
|
16431
16639
|
const name = readManifestMetadata(dto)?.name ?? "(unnamed)";
|
|
16432
16640
|
const triggerType = readPrimaryTriggerType(dto);
|
|
16433
16641
|
if (triggerType !== "schedule") {
|
|
@@ -16435,17 +16643,71 @@ var triggerAutomation = {
|
|
|
16435
16643
|
`"${name}" runs on ${triggerType ?? "no"} triggers and cannot be started by hand: a webhook automation runs when its event arrives, and an api automation needs an input payload this tool cannot supply.`
|
|
16436
16644
|
);
|
|
16437
16645
|
}
|
|
16438
|
-
|
|
16439
|
-
|
|
16440
|
-
|
|
16441
|
-
|
|
16646
|
+
const started = await runApi.trigger(automationId);
|
|
16647
|
+
if (!started) {
|
|
16648
|
+
return {
|
|
16649
|
+
textContent: `Run of "${name}" accepted. It runs in the background; find-runs shows the outcome.`,
|
|
16650
|
+
structuredContent: { automationId, name, outcome: "started" }
|
|
16651
|
+
};
|
|
16652
|
+
}
|
|
16653
|
+
const base = { automationId, name, runId: started.runId };
|
|
16654
|
+
if (waitMinutes === 0) {
|
|
16655
|
+
return {
|
|
16656
|
+
textContent: `Run ${started.runId} of "${name}" started and runs in the background; get-run shows how it ends.`,
|
|
16657
|
+
structuredContent: { ...base, outcome: "started", status: started.status }
|
|
16658
|
+
};
|
|
16442
16659
|
}
|
|
16660
|
+
const timeoutMs = waitMinutes * 6e4;
|
|
16661
|
+
const finished = await waitWithProgress(
|
|
16662
|
+
(onPoll) => waitForRun(runApi, started, { timeoutMs, signal, onPoll }),
|
|
16663
|
+
{
|
|
16664
|
+
run: `Run ${started.runId}`,
|
|
16665
|
+
subject: `Run ${started.runId} of "${name}"`,
|
|
16666
|
+
startedStatus: started.status,
|
|
16667
|
+
recovery: "It keeps running; get-run shows how it ends, so do not trigger it again to check on it.",
|
|
16668
|
+
timeoutMs,
|
|
16669
|
+
signal,
|
|
16670
|
+
reportProgress
|
|
16671
|
+
}
|
|
16672
|
+
);
|
|
16673
|
+
const output = {
|
|
16674
|
+
...base,
|
|
16675
|
+
outcome: finished.outcome,
|
|
16676
|
+
status: finished.status,
|
|
16677
|
+
...finished.failedStep ? {
|
|
16678
|
+
failedStep: {
|
|
16679
|
+
stepId: finished.failedStep.stepId,
|
|
16680
|
+
description: finished.failedStep.description,
|
|
16681
|
+
...finished.failedStep.error ? { error: finished.failedStep.error } : {}
|
|
16682
|
+
}
|
|
16683
|
+
} : {},
|
|
16684
|
+
...finished.error ? { error: finished.error } : {},
|
|
16685
|
+
...finished.result ? { result: finished.result } : {}
|
|
16686
|
+
};
|
|
16443
16687
|
return {
|
|
16444
|
-
textContent:
|
|
16445
|
-
structuredContent:
|
|
16688
|
+
textContent: describeFinished2(output, waitMinutes),
|
|
16689
|
+
structuredContent: output
|
|
16446
16690
|
};
|
|
16447
16691
|
}
|
|
16448
16692
|
};
|
|
16693
|
+
function describeFinished2(output, waitMinutes) {
|
|
16694
|
+
const run = `Run ${output.runId} of "${output.name}"`;
|
|
16695
|
+
switch (output.outcome) {
|
|
16696
|
+
case "completed":
|
|
16697
|
+
return `${run} completed.`;
|
|
16698
|
+
case "failed": {
|
|
16699
|
+
const step = output.failedStep ? ` at step ${output.failedStep.stepId}` : "";
|
|
16700
|
+
const error = output.error ? `: ${output.error}` : "";
|
|
16701
|
+
return `${run} failed${step}${error}. get-run shows every step.`;
|
|
16702
|
+
}
|
|
16703
|
+
case "skipped":
|
|
16704
|
+
return `${run} was skipped${output.error ? `: ${output.error}` : ""}.`;
|
|
16705
|
+
case "cancelled":
|
|
16706
|
+
return `${run} was cancelled.`;
|
|
16707
|
+
case "timed_out":
|
|
16708
|
+
return `${run} was still ${output.status} after ${waitMinutes} min. It keeps running, possibly through a delay step; get-run shows how it ends.`;
|
|
16709
|
+
}
|
|
16710
|
+
}
|
|
16449
16711
|
|
|
16450
16712
|
// src/tools/update-automation.ts
|
|
16451
16713
|
import { z as z93 } from "zod";
|
|
@@ -16595,7 +16857,7 @@ You have access to a user's Todoist Automations through the tools listed.
|
|
|
16595
16857
|
- Every reference is an opaque id taken from a find- or get- tool. There is no lookup by name and no URL parsing: list first, then act on the id. Connector ids are the one exception: they are stable slugs such as github or slack.
|
|
16596
16858
|
- An automation belongs either to the user's personal space or to one Todoist workspace, and the two are separate. Pass workspaceId to reach workspace automations; omit it for personal ones.
|
|
16597
16859
|
- update-automation, set-automation-schedule, set-automation-visibility, move-automation, trigger-automation, delete-automation, update-draft, connect-draft, publish-draft, delete-draft and disconnect-service act immediately: no preview, and no confirmation of their own. Read the target first (get-automation, get-draft for a draft, or get-connector for a connection) so it is the right one, and confirm intent with the user before changing, running or deleting anything they did not explicitly ask for.
|
|
16598
|
-
- trigger-automation
|
|
16860
|
+
- trigger-automation waits for the run to end (waitMinutes, default 10); a run still going then keeps running, and get-run reads it.
|
|
16599
16861
|
- A workspace automation is either visible to the whole workspace or private to its creator; isOwner says whether the user created it, and createdBy gives the creator's Todoist user id. Another member's automation is read-only for the user, but trigger-automation still works on it and the run executes on its creator's connections; find-runs shows who started each run as triggeredBy.
|
|
16600
16862
|
- A workspace admin can update-automation, set-automation-visibility and delete-automation on another member's team-visible automation, but cannot reschedule it, change what it does (that needs its creator's draft or conversation) or reach a private one. updatedBy on an automation tells the creator's own change from an admin's.
|
|
16601
16863
|
- move-automation puts an automation the user created into a workspace, from the personal space or another workspace, and never back out. Connections stay personal in every space: a moved or shared automation keeps running on its creator's own connections.
|
|
@@ -16633,4 +16895,4 @@ export {
|
|
|
16633
16895
|
DEFAULT_BASE_URL,
|
|
16634
16896
|
getMcpServer
|
|
16635
16897
|
};
|
|
16636
|
-
//# sourceMappingURL=chunk-
|
|
16898
|
+
//# sourceMappingURL=chunk-UZ45CAIT.js.map
|