@doist/automations-mcp 1.12.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()
|
|
@@ -11203,13 +11208,19 @@ var AutomationsRequestError = class extends Error {
|
|
|
11203
11208
|
* scope: a challenge can say the grant falls short without saying of what.
|
|
11204
11209
|
*/
|
|
11205
11210
|
requiredScope;
|
|
11206
|
-
|
|
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) {
|
|
11207
11217
|
super(message);
|
|
11208
11218
|
this.name = "AutomationsRequestError";
|
|
11209
11219
|
this.httpStatusCode = httpStatusCode;
|
|
11210
11220
|
this.responseData = responseData;
|
|
11211
11221
|
this.authChallenge = authChallenge;
|
|
11212
11222
|
this.requiredScope = parseChallengeScope(authChallenge);
|
|
11223
|
+
this.requestId = requestId;
|
|
11213
11224
|
}
|
|
11214
11225
|
isAuthError() {
|
|
11215
11226
|
return this.httpStatusCode === 401 || this.httpStatusCode === 403;
|
|
@@ -11280,7 +11291,7 @@ async function request(args) {
|
|
|
11280
11291
|
const data = await readResponseBody(response);
|
|
11281
11292
|
if (!response.ok) {
|
|
11282
11293
|
const message = typeof data === "object" && data !== null && "message" in data ? String(data.message) : response.statusText || `Request failed with status ${response.status}`;
|
|
11283
|
-
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);
|
|
11284
11295
|
}
|
|
11285
11296
|
return data;
|
|
11286
11297
|
}
|
|
@@ -11700,13 +11711,15 @@ var AutomationsClient = class extends BaseClient {
|
|
|
11700
11711
|
});
|
|
11701
11712
|
return createDraftResponseSchema.parse(data);
|
|
11702
11713
|
}
|
|
11714
|
+
/** Starts a run now. Undefined from an API that predates returning the run. */
|
|
11703
11715
|
async trigger(id, options) {
|
|
11704
11716
|
const parsedId = IdSchema.parse(id);
|
|
11705
|
-
await this.request({
|
|
11717
|
+
const data = await this.request({
|
|
11706
11718
|
method: "POST",
|
|
11707
11719
|
path: automationsEndpoints.trigger(parsedId),
|
|
11708
11720
|
options
|
|
11709
11721
|
});
|
|
11722
|
+
return triggerAutomationResponseSchema.optional().parse(data);
|
|
11710
11723
|
}
|
|
11711
11724
|
async test(id, options) {
|
|
11712
11725
|
const parsedId = IdSchema.parse(id);
|
|
@@ -12041,13 +12054,17 @@ var WorkspaceAutomationsClient = class extends BaseClient {
|
|
|
12041
12054
|
});
|
|
12042
12055
|
return automationSchema.parse(data);
|
|
12043
12056
|
}
|
|
12044
|
-
/**
|
|
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
|
+
*/
|
|
12045
12061
|
async trigger(workspaceId, id, options) {
|
|
12046
|
-
await this.request({
|
|
12062
|
+
const data = await this.request({
|
|
12047
12063
|
method: "POST",
|
|
12048
12064
|
path: workspaceAutomationsEndpoints.trigger(IdSchema.parse(workspaceId), IdSchema.parse(id)),
|
|
12049
12065
|
options
|
|
12050
12066
|
});
|
|
12067
|
+
return triggerAutomationResponseSchema.optional().parse(data);
|
|
12051
12068
|
}
|
|
12052
12069
|
/**
|
|
12053
12070
|
* Deletes the automation. Workspace admins may delete another member's
|
|
@@ -13452,31 +13469,11 @@ function readManifestName(manifest) {
|
|
|
13452
13469
|
return typeof name === "string" && name.trim() ? name : "(unnamed)";
|
|
13453
13470
|
}
|
|
13454
13471
|
|
|
13455
|
-
// ../../packages/sdk/dist/esm/
|
|
13456
|
-
var DRAFT_TEST_OUTCOMES = [
|
|
13457
|
-
"passed",
|
|
13458
|
-
"failed",
|
|
13459
|
-
"skipped",
|
|
13460
|
-
"cancelled",
|
|
13461
|
-
"timed_out"
|
|
13462
|
-
];
|
|
13472
|
+
// ../../packages/sdk/dist/esm/runs/poll-until-closed.js
|
|
13463
13473
|
var DEFAULT_POLL_INTERVALS_MS = { first: 2e3, max: 1e4 };
|
|
13464
13474
|
var STATUS_REQUEST_GRACE_MS = 1e4;
|
|
13465
|
-
var OPEN_RUN_STATUSES = /* @__PURE__ */ new Set([
|
|
13466
|
-
|
|
13467
|
-
"running"
|
|
13468
|
-
]);
|
|
13469
|
-
async function startDraftTest(draftApi, { draftId, request: request2 }) {
|
|
13470
|
-
const started = await draftApi.test(draftId, request2);
|
|
13471
|
-
return {
|
|
13472
|
-
draftId,
|
|
13473
|
-
testRunId: started.testRunId,
|
|
13474
|
-
trigger: started.trigger,
|
|
13475
|
-
connectorCalls: started.connectorCalls,
|
|
13476
|
-
status: started.status
|
|
13477
|
-
};
|
|
13478
|
-
}
|
|
13479
|
-
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 }) {
|
|
13480
13477
|
const intervals = pollIntervalsMs ?? DEFAULT_POLL_INTERVALS_MS;
|
|
13481
13478
|
const deadline = Date.now() + timeoutMs;
|
|
13482
13479
|
let intervalMs = intervals.first;
|
|
@@ -13485,24 +13482,64 @@ async function waitForDraftTest(draftApi, { draftId, testRunId }, { timeoutMs, p
|
|
|
13485
13482
|
let last = null;
|
|
13486
13483
|
while (true) {
|
|
13487
13484
|
try {
|
|
13488
|
-
last = await
|
|
13485
|
+
last = await read(requestSignal);
|
|
13489
13486
|
} catch (error) {
|
|
13490
13487
|
if (signal.aborted || !requestTimeout.aborted)
|
|
13491
13488
|
throw error;
|
|
13492
|
-
return
|
|
13489
|
+
return { timedOut: true, run: last };
|
|
13493
13490
|
}
|
|
13494
13491
|
if (!OPEN_RUN_STATUSES.has(last.status)) {
|
|
13495
|
-
return
|
|
13492
|
+
return { timedOut: false, run: last };
|
|
13496
13493
|
}
|
|
13497
13494
|
onPoll?.(last);
|
|
13498
13495
|
const remainingMs = deadline - Date.now();
|
|
13499
13496
|
if (remainingMs <= 0) {
|
|
13500
|
-
return
|
|
13497
|
+
return { timedOut: true, run: last };
|
|
13501
13498
|
}
|
|
13502
13499
|
await delay(Math.min(intervalMs, remainingMs), signal);
|
|
13503
13500
|
intervalMs = Math.min(intervalMs * 2, intervals.max);
|
|
13504
13501
|
}
|
|
13505
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
|
+
}
|
|
13506
13543
|
function toDraftTestTriggerSelector(value) {
|
|
13507
13544
|
if (typeof value === "number")
|
|
13508
13545
|
return { index: value };
|
|
@@ -13529,22 +13566,65 @@ function toOutcome(status) {
|
|
|
13529
13566
|
return "failed";
|
|
13530
13567
|
}
|
|
13531
13568
|
}
|
|
13532
|
-
|
|
13533
|
-
|
|
13534
|
-
|
|
13535
|
-
|
|
13536
|
-
|
|
13537
|
-
|
|
13538
|
-
|
|
13539
|
-
|
|
13540
|
-
|
|
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
|
+
})
|
|
13541
13580
|
};
|
|
13542
|
-
|
|
13543
|
-
|
|
13544
|
-
|
|
13545
|
-
|
|
13546
|
-
|
|
13547
|
-
|
|
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;
|
|
13548
13628
|
}
|
|
13549
13629
|
|
|
13550
13630
|
// ../../packages/sdk/dist/esm/chat-turn/connection-requirements.js
|
|
@@ -13936,6 +14016,67 @@ function assertSelectionsUnambiguous(requirements, selections) {
|
|
|
13936
14016
|
|
|
13937
14017
|
// ../../packages/sdk/dist/esm/chat-turn/transport.js
|
|
13938
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
|
|
13939
14080
|
var ChatTurnTransport = class {
|
|
13940
14081
|
transport;
|
|
13941
14082
|
constructor({ token, baseUrl, headers, fetch: fetch2 }) {
|
|
@@ -13962,39 +14103,62 @@ var ChatTurnTransport = class {
|
|
|
13962
14103
|
}
|
|
13963
14104
|
/** Streams one server turn, reporting incremental text and deduplicated tool state changes. */
|
|
13964
14105
|
async streamTurn({ conversationId, workspaceId = null, message, signal, onText, onToolActivity }) {
|
|
13965
|
-
const
|
|
13966
|
-
|
|
13967
|
-
|
|
13968
|
-
|
|
13969
|
-
|
|
13970
|
-
|
|
13971
|
-
|
|
13972
|
-
|
|
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
|
+
}
|
|
13973
14122
|
let finalMessage;
|
|
13974
14123
|
let emittedText = "";
|
|
13975
14124
|
const emittedToolStates = /* @__PURE__ */ new Set();
|
|
13976
|
-
|
|
13977
|
-
|
|
13978
|
-
|
|
13979
|
-
|
|
13980
|
-
|
|
13981
|
-
|
|
13982
|
-
|
|
13983
|
-
|
|
13984
|
-
|
|
13985
|
-
|
|
13986
|
-
|
|
13987
|
-
|
|
13988
|
-
|
|
13989
|
-
|
|
13990
|
-
|
|
13991
|
-
|
|
13992
|
-
|
|
13993
|
-
|
|
13994
|
-
|
|
13995
|
-
|
|
13996
|
-
|
|
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
|
+
}
|
|
13997
14157
|
}
|
|
14158
|
+
} catch (error) {
|
|
14159
|
+
if (callbackError)
|
|
14160
|
+
throw callbackError.error;
|
|
14161
|
+
throw fail("stream", error);
|
|
13998
14162
|
}
|
|
13999
14163
|
if (!finalMessage) {
|
|
14000
14164
|
throw new Error("The Automations chat returned an empty response.");
|
|
@@ -14019,7 +14183,7 @@ import { McpServer } from "@modelcontextprotocol/server";
|
|
|
14019
14183
|
// package.json
|
|
14020
14184
|
var package_default = {
|
|
14021
14185
|
name: "@doist/automations-mcp",
|
|
14022
|
-
version: "1.
|
|
14186
|
+
version: "1.13.0",
|
|
14023
14187
|
description: "MCP server and importable AI tools for Todoist Automations",
|
|
14024
14188
|
type: "module",
|
|
14025
14189
|
main: "./dist/index.js",
|
|
@@ -14079,7 +14243,7 @@ var package_default = {
|
|
|
14079
14243
|
"@types/node": "24.19.2",
|
|
14080
14244
|
"conventional-changelog-conventionalcommits": "9.3.1",
|
|
14081
14245
|
esbuild: "0.28.2",
|
|
14082
|
-
"gpt-tokenizer": "4.0
|
|
14246
|
+
"gpt-tokenizer": "4.1.0",
|
|
14083
14247
|
"semantic-release": "25.0.9",
|
|
14084
14248
|
tsx: "4.23.15",
|
|
14085
14249
|
typescript: "6.0.3",
|
|
@@ -15144,6 +15308,7 @@ function startProgressTicker(reportProgress, message, intervalMs = PROGRESS_INTE
|
|
|
15144
15308
|
|
|
15145
15309
|
// src/utils/chat-turn.ts
|
|
15146
15310
|
var TEXT_PREVIEW_LENGTH = 120;
|
|
15311
|
+
var DRAFT_LOOKUP_TIMEOUT_MS = 5e3;
|
|
15147
15312
|
async function runTurn({
|
|
15148
15313
|
reportProgress,
|
|
15149
15314
|
progressIntervalMs = PROGRESS_INTERVAL_MS,
|
|
@@ -15170,8 +15335,9 @@ async function runTurn({
|
|
|
15170
15335
|
onToolActivity: ({ tool, state }) => reportProgress(`${tool}: ${state}`)
|
|
15171
15336
|
});
|
|
15172
15337
|
} catch (error) {
|
|
15338
|
+
stopProgress();
|
|
15173
15339
|
throw new Error(
|
|
15174
|
-
`Conversation ${turn.conversationId}: ${describeTurnError(error, turn
|
|
15340
|
+
`Conversation ${turn.conversationId}: ${await describeTurnError(error, turn)}`
|
|
15175
15341
|
);
|
|
15176
15342
|
} finally {
|
|
15177
15343
|
stopProgress();
|
|
@@ -15186,8 +15352,8 @@ function describeConversationError(conversationId) {
|
|
|
15186
15352
|
throw new Error(`Conversation ${conversationId}: ${formatToolError(error)}`);
|
|
15187
15353
|
};
|
|
15188
15354
|
}
|
|
15189
|
-
function describeTurnError(error,
|
|
15190
|
-
if (signal?.aborted) {
|
|
15355
|
+
async function describeTurnError(error, turn) {
|
|
15356
|
+
if (turn.signal?.aborted) {
|
|
15191
15357
|
return "the turn was cancelled before it finished. Whatever the assistant had said is saved; get-conversation shows it and continue-conversation resumes.";
|
|
15192
15358
|
}
|
|
15193
15359
|
if (error instanceof ChatTurnInputError) {
|
|
@@ -15204,11 +15370,28 @@ function describeTurnError(error, signal) {
|
|
|
15204
15370
|
return error.message;
|
|
15205
15371
|
}
|
|
15206
15372
|
}
|
|
15207
|
-
const refusal =
|
|
15373
|
+
const refusal = getChatTurnBillingRefusal(error)?.message;
|
|
15208
15374
|
if (refusal !== void 0) {
|
|
15209
15375
|
return `the turn was refused. ${refusal.endsWith(".") ? refusal : `${refusal}.`} Retrying won't help until the user acts.`;
|
|
15210
15376
|
}
|
|
15211
|
-
|
|
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);
|
|
15212
15395
|
}
|
|
15213
15396
|
function describeTurnResult(result) {
|
|
15214
15397
|
const next = describeNextStep(result);
|
|
@@ -16278,6 +16461,37 @@ var startConversation = {
|
|
|
16278
16461
|
|
|
16279
16462
|
// src/tools/test-draft.ts
|
|
16280
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
|
|
16281
16495
|
var DEFAULT_WAIT_MINUTES = 10;
|
|
16282
16496
|
var parameters27 = {
|
|
16283
16497
|
draftId: z91.string().describe("The draft to test, as listed by find-drafts or get-draft."),
|
|
@@ -16334,11 +16548,23 @@ var testDraft = {
|
|
|
16334
16548
|
structuredContent: { ...base, outcome: "started", status: started.status }
|
|
16335
16549
|
};
|
|
16336
16550
|
}
|
|
16337
|
-
const
|
|
16338
|
-
|
|
16339
|
-
|
|
16340
|
-
|
|
16341
|
-
|
|
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
|
+
);
|
|
16342
16568
|
const output = {
|
|
16343
16569
|
...base,
|
|
16344
16570
|
outcome: finished.outcome,
|
|
@@ -16359,36 +16585,6 @@ var testDraft = {
|
|
|
16359
16585
|
};
|
|
16360
16586
|
}
|
|
16361
16587
|
};
|
|
16362
|
-
async function waitWithProgress(draftApi, started, {
|
|
16363
|
-
timeoutMs,
|
|
16364
|
-
signal,
|
|
16365
|
-
reportProgress
|
|
16366
|
-
}) {
|
|
16367
|
-
const run = `Test run ${started.testRunId}`;
|
|
16368
|
-
let state = started.status;
|
|
16369
|
-
reportProgress(`${run} started; waiting up to ${timeoutMs / 6e4} min`);
|
|
16370
|
-
const stopProgress = startProgressTicker(
|
|
16371
|
-
reportProgress,
|
|
16372
|
-
(elapsedSeconds) => `${run}: ${state} (${elapsedSeconds}s)`
|
|
16373
|
-
);
|
|
16374
|
-
try {
|
|
16375
|
-
return await waitForDraftTest(draftApi, started, {
|
|
16376
|
-
timeoutMs,
|
|
16377
|
-
signal,
|
|
16378
|
-
onPoll: (polled) => {
|
|
16379
|
-
const step = polled.steps.find((candidate) => candidate.status === "running");
|
|
16380
|
-
state = step ? `running ${step.stepId}` : polled.status;
|
|
16381
|
-
}
|
|
16382
|
-
});
|
|
16383
|
-
} catch (error) {
|
|
16384
|
-
const why = signal.aborted ? "the wait was cancelled." : `checking on it failed: ${formatToolError(error).replace(/\.?$/, ".")}`;
|
|
16385
|
-
throw new Error(
|
|
16386
|
-
`${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.`
|
|
16387
|
-
);
|
|
16388
|
-
} finally {
|
|
16389
|
-
stopProgress();
|
|
16390
|
-
}
|
|
16391
|
-
}
|
|
16392
16588
|
function describeFinished(output, waitMinutes) {
|
|
16393
16589
|
const run = `Test run ${output.testRunId} of draft ${output.draftId}`;
|
|
16394
16590
|
const otherTriggers = output.trigger.count > 1 ? ` It tested trigger ${output.trigger.index} of ${output.trigger.count}; pass trigger to test another.` : "";
|
|
@@ -16411,24 +16607,35 @@ function describeFinished(output, waitMinutes) {
|
|
|
16411
16607
|
|
|
16412
16608
|
// src/tools/trigger-automation.ts
|
|
16413
16609
|
import { z as z92 } from "zod";
|
|
16610
|
+
var DEFAULT_WAIT_MINUTES2 = 10;
|
|
16414
16611
|
var parameters28 = {
|
|
16415
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
|
+
),
|
|
16416
16616
|
workspaceId: workspaceIdParameter
|
|
16417
16617
|
};
|
|
16418
16618
|
var outputSchema26 = {
|
|
16419
16619
|
automationId: z92.string(),
|
|
16420
16620
|
name: z92.string(),
|
|
16421
|
-
|
|
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
|
|
16422
16629
|
};
|
|
16423
16630
|
var triggerAutomation = {
|
|
16424
16631
|
name: ToolNames.TRIGGER_AUTOMATION,
|
|
16425
|
-
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.",
|
|
16426
16633
|
parameters: parameters28,
|
|
16427
16634
|
outputSchema: outputSchema26,
|
|
16428
16635
|
annotations: { readOnlyHint: false, destructiveHint: false, idempotentHint: false },
|
|
16429
|
-
async execute({ automationId, workspaceId }, { api, signal }) {
|
|
16430
|
-
const
|
|
16431
|
-
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);
|
|
16432
16639
|
const name = readManifestMetadata(dto)?.name ?? "(unnamed)";
|
|
16433
16640
|
const triggerType = readPrimaryTriggerType(dto);
|
|
16434
16641
|
if (triggerType !== "schedule") {
|
|
@@ -16436,17 +16643,71 @@ var triggerAutomation = {
|
|
|
16436
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.`
|
|
16437
16644
|
);
|
|
16438
16645
|
}
|
|
16439
|
-
|
|
16440
|
-
|
|
16441
|
-
|
|
16442
|
-
|
|
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
|
+
};
|
|
16443
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
|
+
};
|
|
16444
16687
|
return {
|
|
16445
|
-
textContent:
|
|
16446
|
-
structuredContent:
|
|
16688
|
+
textContent: describeFinished2(output, waitMinutes),
|
|
16689
|
+
structuredContent: output
|
|
16447
16690
|
};
|
|
16448
16691
|
}
|
|
16449
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
|
+
}
|
|
16450
16711
|
|
|
16451
16712
|
// src/tools/update-automation.ts
|
|
16452
16713
|
import { z as z93 } from "zod";
|
|
@@ -16596,7 +16857,7 @@ You have access to a user's Todoist Automations through the tools listed.
|
|
|
16596
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.
|
|
16597
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.
|
|
16598
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.
|
|
16599
|
-
- 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.
|
|
16600
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.
|
|
16601
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.
|
|
16602
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.
|
|
@@ -16634,4 +16895,4 @@ export {
|
|
|
16634
16895
|
DEFAULT_BASE_URL,
|
|
16635
16896
|
getMcpServer
|
|
16636
16897
|
};
|
|
16637
|
-
//# sourceMappingURL=chunk-
|
|
16898
|
+
//# sourceMappingURL=chunk-UZ45CAIT.js.map
|