@rallycry/conveyor-mcp 5.0.4 → 5.0.6

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.
@@ -574,6 +574,52 @@ var ConveyorConnection = class {
574
574
  ...rest
575
575
  });
576
576
  }
577
+ // ── Decisions ───────────────────────────────────────────────────────
578
+ //
579
+ // Every method resolves the project the same way the rest of the surface
580
+ // does, so a caller with a configured default never has to pass one.
581
+ createDecision(params) {
582
+ const { projectId, ...rest } = params;
583
+ return this.call("createProjectDecision", {
584
+ projectId: this.resolveProjectId(projectId),
585
+ ...rest
586
+ });
587
+ }
588
+ listDecisions(params) {
589
+ const { projectId, ...rest } = params;
590
+ return this.call("listProjectDecisions", {
591
+ projectId: this.resolveProjectId(projectId),
592
+ ...rest
593
+ });
594
+ }
595
+ getDecision(params) {
596
+ const { projectId, ...rest } = params;
597
+ return this.call("getProjectDecision", {
598
+ projectId: this.resolveProjectId(projectId),
599
+ ...rest
600
+ });
601
+ }
602
+ voteDecision(params) {
603
+ const { projectId, ...rest } = params;
604
+ return this.call("voteProjectDecision", {
605
+ projectId: this.resolveProjectId(projectId),
606
+ ...rest
607
+ });
608
+ }
609
+ resolveDecision(params) {
610
+ const { projectId, ...rest } = params;
611
+ return this.call("resolveProjectDecision", {
612
+ projectId: this.resolveProjectId(projectId),
613
+ ...rest
614
+ });
615
+ }
616
+ updateDecision(params) {
617
+ const { projectId, ...rest } = params;
618
+ return this.call("updateProjectDecision", {
619
+ projectId: this.resolveProjectId(projectId),
620
+ ...rest
621
+ });
622
+ }
577
623
  // ── Meetings ────────────────────────────────────────────────────────
578
624
  listMeetings(params) {
579
625
  const { projectId, ...rest } = params;
package/dist/cli.js CHANGED
@@ -4,7 +4,7 @@ import {
4
4
  } from "./chunk-5KU3QS6A.js";
5
5
  import {
6
6
  ConveyorConnection
7
- } from "./chunk-EUCQ26F7.js";
7
+ } from "./chunk-77XLXIFP.js";
8
8
 
9
9
  // src/cli.ts
10
10
  import { createRequire } from "module";
@@ -72,7 +72,7 @@ function registerProjectTools(server2, conn2) {
72
72
  // src/tools/connection.ts
73
73
  import { z as z4 } from "zod";
74
74
 
75
- // ../shared/dist/chunk-VQSAISHG.js
75
+ // ../shared/dist/chunk-6NPOGQPP.js
76
76
  import { z as z2 } from "zod";
77
77
  var DEFAULT_SONNET_MODEL = "claude-sonnet-5";
78
78
  var DEFAULT_OPUS_MODEL = "claude-opus-5";
@@ -306,14 +306,14 @@ var f = {
306
306
  return { kind: "nullable", inner };
307
307
  }
308
308
  };
309
- function compileString(z11, spec) {
310
- let schema = z11.string();
309
+ function compileString(z13, spec) {
310
+ let schema = z13.string();
311
311
  if (spec.min !== void 0) schema = schema.min(spec.min);
312
312
  if (spec.max !== void 0) schema = schema.max(spec.max);
313
313
  return schema;
314
314
  }
315
- function compileNumber(z11, spec) {
316
- let schema = z11.number();
315
+ function compileNumber(z13, spec) {
316
+ let schema = z13.number();
317
317
  if (spec.int) schema = schema.int();
318
318
  if (spec.positive) schema = schema.positive();
319
319
  if (spec.nonnegative) schema = schema.nonnegative();
@@ -321,49 +321,49 @@ function compileNumber(z11, spec) {
321
321
  if (spec.max !== void 0) schema = schema.max(spec.max);
322
322
  return schema;
323
323
  }
324
- function compileArray(z11, spec) {
325
- let schema = z11.array(compileField(z11, spec.item));
324
+ function compileArray(z13, spec) {
325
+ let schema = z13.array(compileField(z13, spec.item));
326
326
  if (spec.min !== void 0) schema = schema.min(spec.min);
327
327
  return schema;
328
328
  }
329
- function compileBase(z11, spec) {
329
+ function compileBase(z13, spec) {
330
330
  switch (spec.kind) {
331
331
  case "string":
332
- return compileString(z11, spec);
332
+ return compileString(z13, spec);
333
333
  case "number":
334
- return compileNumber(z11, spec);
334
+ return compileNumber(z13, spec);
335
335
  case "boolean":
336
- return z11.boolean();
336
+ return z13.boolean();
337
337
  case "enum":
338
- return z11.enum([...spec.values]);
338
+ return z13.enum([...spec.values]);
339
339
  case "array":
340
- return compileArray(z11, spec);
340
+ return compileArray(z13, spec);
341
341
  case "object":
342
- return z11.object(compileShape(z11, spec.fields));
342
+ return z13.object(compileShape(z13, spec.fields));
343
343
  }
344
344
  }
345
345
  function descriptionOf(spec) {
346
346
  if (spec.kind === "optional" || spec.kind === "nullable") return descriptionOf(spec.inner);
347
347
  return spec.desc;
348
348
  }
349
- function compileUndescribed(z11, spec) {
349
+ function compileUndescribed(z13, spec) {
350
350
  if (spec.kind === "optional") {
351
- return compileUndescribed(z11, spec.inner).optional();
351
+ return compileUndescribed(z13, spec.inner).optional();
352
352
  }
353
353
  if (spec.kind === "nullable") {
354
- return compileUndescribed(z11, spec.inner).nullable();
354
+ return compileUndescribed(z13, spec.inner).nullable();
355
355
  }
356
- return compileBase(z11, spec);
356
+ return compileBase(z13, spec);
357
357
  }
358
- function compileField(z11, spec) {
359
- const schema = compileUndescribed(z11, spec);
358
+ function compileField(z13, spec) {
359
+ const schema = compileUndescribed(z13, spec);
360
360
  const desc = descriptionOf(spec);
361
361
  return desc === void 0 ? schema : schema.describe(desc);
362
362
  }
363
- function compileShape(z11, fields) {
363
+ function compileShape(z13, fields) {
364
364
  const shape = {};
365
365
  for (const [key, spec] of Object.entries(fields)) {
366
- shape[key] = compileField(z11, spec);
366
+ shape[key] = compileField(z13, spec);
367
367
  }
368
368
  return shape;
369
369
  }
@@ -476,7 +476,7 @@ var SEARCH_STATUSES = [
476
476
  "Complete",
477
477
  "Cancelled"
478
478
  ];
479
- var SEARCH_CARD_TYPES = ["task", "incident", "suggestion"];
479
+ var SEARCH_CARD_TYPES = ["task", "incident", "suggestion", "decision"];
480
480
  var searchFilterFields = {
481
481
  tagNames: f.optional(
482
482
  f.array(f.string(), {
@@ -1118,6 +1118,11 @@ var uploadAttachmentContract = defineToolContract({
1118
1118
  title: f.optional(
1119
1119
  f.string({ desc: "Short caption posted with the file (defaults to the file name)" })
1120
1120
  ),
1121
+ comment: f.optional(
1122
+ f.string({
1123
+ desc: "Alias of `title` (the external conveyor-mcp surface names this field `comment`). Provide at most one of the two."
1124
+ })
1125
+ ),
1121
1126
  tags: attachmentTags
1122
1127
  }
1123
1128
  },
@@ -1130,6 +1135,11 @@ var uploadAttachmentContract = defineToolContract({
1130
1135
  comment: f.optional(
1131
1136
  f.string({ desc: "When set, also posts the attachment to the task chat with this text" })
1132
1137
  ),
1138
+ title: f.optional(
1139
+ f.string({
1140
+ desc: "Alias of `comment` (the in-pod agent surface names this field `title`). Provide at most one of the two."
1141
+ })
1142
+ ),
1133
1143
  tags: attachmentTags,
1134
1144
  mimeType: f.optional(
1135
1145
  f.string({ desc: "Override the mime type inferred from the file extension" })
@@ -1169,6 +1179,293 @@ var createSuggestionContract = defineToolContract({
1169
1179
  }
1170
1180
  });
1171
1181
  var suggestionsContracts = [createSuggestionContract];
1182
+ var OPTION_KINDS = ["current", "planned", "proposed"];
1183
+ var STATUS_LABELS = ["Open", "Decided", "Cancelled"];
1184
+ var optionDescription = "What this option means in the product, written as GIVEN / WHEN / THEN. Concrete enough that a reader six months from now can tell which behaviour was chosen.";
1185
+ var optionsDesc = "Two or three options. More than three means the question is not framed yet \u2014 narrow it or split it. Mark the option describing what the code does today as kind: current.";
1186
+ var defaultOptionDesc = "1-based position of the option that wins if nobody decides by the deadline. Required: silence has to become a decision, never a stall.";
1187
+ var agentOption = f.object(
1188
+ {
1189
+ label: f.string({ min: 1, desc: "Short name for the option, under 80 characters" }),
1190
+ description: f.string({ min: 1, desc: optionDescription }),
1191
+ kind: f.optional(
1192
+ f.enum(OPTION_KINDS, {
1193
+ desc: 'current = what the code does today, planned = already agreed but unbuilt, proposed = new. Defaults to "proposed".'
1194
+ })
1195
+ )
1196
+ },
1197
+ { desc: "One option people can choose" }
1198
+ );
1199
+ var createDecisionContract = defineToolContract({
1200
+ name: "create_decision",
1201
+ agent: {
1202
+ description: `Raise a product decision: a fork the plan cannot be right without. Two or three options, one default, a deadline. Run list_decisions FIRST \u2014 an answered decision is the answer, and re-asking it wastes everybody's time. Do NOT file a decision for an assumption you can simply state: an assumed behaviour belongs in the plan as a GIVEN / WHEN / THEN line under "Assumed behaviours". Returns the decision in full (the same JSON as get_decision, with option ids and the deadline) plus which cards were linked.`,
1203
+ fields: {
1204
+ question: f.string({
1205
+ min: 1,
1206
+ desc: "The question, as one plain sentence ending in a question mark"
1207
+ }),
1208
+ summary: f.optional(
1209
+ f.string({
1210
+ desc: cardDescriptionDesc("One or two sentences of context for the card face")
1211
+ })
1212
+ ),
1213
+ context: f.string({
1214
+ min: 1,
1215
+ desc: "Markdown background: why this is being asked now, and what breaks if it is answered wrongly"
1216
+ }),
1217
+ options: f.array(agentOption, { min: 2, desc: optionsDesc }),
1218
+ default_option: f.number({ int: true, min: 1, max: 3, desc: defaultOptionDesc }),
1219
+ deadline_hours: f.optional(
1220
+ f.number({ min: 1, max: 720, desc: "Hours until the default applies. Default 72." })
1221
+ ),
1222
+ impacted_cards: f.optional(
1223
+ f.array(f.string(), {
1224
+ desc: "Slugs or ids of cards this decision blocks. They cannot start until it is settled, and they are un-blocked the moment it is. Blocking a card needs Moderate access: without it the decision is still raised and each ref comes back under refused."
1225
+ })
1226
+ ),
1227
+ include_current_card: f.optional(
1228
+ f.boolean({
1229
+ desc: "Also block the card this session is working on. Defaults to true \u2014 you are raising this because your own plan is stuck on it. Pass false to raise the question without stopping your own work."
1230
+ })
1231
+ ),
1232
+ tag_names: f.optional(
1233
+ f.array(f.string(), {
1234
+ desc: "Tag names. The settled decision is written into each tag's overview, which is how the next agent finds it."
1235
+ })
1236
+ ),
1237
+ raised_by_pr: f.optional(
1238
+ f.number({ int: true, min: 1, desc: "PR number that surfaced the question" })
1239
+ ),
1240
+ raised_by_card: f.optional(
1241
+ f.string({
1242
+ desc: "Slug or id of the card that raised the question. A link back, not a block \u2014 use impacted_cards to block a card."
1243
+ })
1244
+ ),
1245
+ deadline_at: f.optional(
1246
+ f.string({
1247
+ desc: "Absolute deadline as an ISO 8601 timestamp, e.g. 2026-09-25T17:00:00Z. Wins over deadline_hours when both are given; must be in the future and within 720 hours."
1248
+ })
1249
+ )
1250
+ }
1251
+ },
1252
+ mcp: {
1253
+ description: "Raise a product decision on a Conveyor project: a fork that has to be settled before the work can be planned correctly. Two or three options, one default that applies if nobody decides, and a deadline. Search list_decisions first \u2014 a Decided decision is the answer. Pass projectId to target a specific project; otherwise the configured default project is used.",
1254
+ fields: {
1255
+ projectId: mcpProjectId,
1256
+ question: f.string({ desc: "The question, as one plain sentence" }),
1257
+ summary: f.optional(f.string({ desc: cardDescriptionDesc("Context for the card face") })),
1258
+ context: f.string({ desc: "Markdown background: why this is being asked now" }),
1259
+ options: f.array(agentOption, { min: 2, desc: optionsDesc }),
1260
+ defaultOption: f.number({ int: true, min: 1, max: 3, desc: defaultOptionDesc }),
1261
+ deadlineHours: f.optional(
1262
+ f.number({ min: 1, max: 720, desc: "Hours until the default applies. Default 72." })
1263
+ ),
1264
+ impactedCards: f.optional(
1265
+ f.array(f.string(), {
1266
+ desc: "Slugs or ids of cards this decision blocks. Needs Moderate access; without it each ref comes back under refused and the decision is still raised."
1267
+ })
1268
+ ),
1269
+ tagNames: f.optional(f.array(f.string(), { desc: "Tag names to categorize the decision" })),
1270
+ raisedByPr: f.optional(
1271
+ f.number({ int: true, min: 1, desc: "PR number that surfaced the question" })
1272
+ ),
1273
+ raisedByCard: f.optional(
1274
+ f.string({
1275
+ desc: "Slug or id of the card that raised the question. A link back, not a block \u2014 use impactedCards to block a card."
1276
+ })
1277
+ ),
1278
+ deadlineAt: f.optional(
1279
+ f.string({
1280
+ desc: "Absolute deadline (ISO 8601). Wins over deadlineHours when both are given; must be in the future and within 720 hours."
1281
+ })
1282
+ )
1283
+ }
1284
+ }
1285
+ });
1286
+ var listDecisionsContract = defineToolContract({
1287
+ name: "list_decisions",
1288
+ agent: {
1289
+ description: `Search this project's decisions. Run this BEFORE planning anything in an area: "have we already decided how X behaves?" Each row carries the summary, who raised it (and from which card or PR), when, and \u2014 once settled \u2014 the resolution, so the list doubles as the decision log. Filter by card to see what is blocking a specific card.`,
1290
+ fields: {
1291
+ status: f.optional(
1292
+ f.enum(STATUS_LABELS, {
1293
+ desc: "Open = still being voted on, Decided = settled, Cancelled = dropped"
1294
+ })
1295
+ ),
1296
+ tag: f.optional(f.string({ desc: 'Tag name, e.g. "slack"' })),
1297
+ card: f.optional(
1298
+ f.string({ desc: "Slug or id of a card \u2014 returns the decisions it is blocked by" })
1299
+ ),
1300
+ query: f.optional(f.string({ desc: "Text search on the question" })),
1301
+ limit: f.optional(f.number({ desc: "Max rows to return (default 50, max 200)" }))
1302
+ }
1303
+ },
1304
+ mcp: {
1305
+ description: `List and search a Conveyor project's decisions. Decided rows include their resolution, so this is the fastest way to answer "did we already settle this?". Pass projectId to target a specific project; otherwise the configured default project is used.`,
1306
+ fields: {
1307
+ projectId: mcpProjectId,
1308
+ status: f.optional(f.enum(STATUS_LABELS, { desc: "Filter by state" })),
1309
+ tag: f.optional(f.string({ desc: "Tag name to filter by" })),
1310
+ card: f.optional(f.string({ desc: "Card slug or id \u2014 decisions blocking that card" })),
1311
+ query: f.optional(f.string({ desc: "Text search on the question" })),
1312
+ limit: f.optional(
1313
+ f.number({ int: true, min: 1, max: 200, desc: "Max rows to return (default 50)" })
1314
+ )
1315
+ }
1316
+ }
1317
+ });
1318
+ var getDecisionContract = defineToolContract({
1319
+ name: "get_decision",
1320
+ agent: {
1321
+ description: "Read one decision in full: its summary and context, who raised it and from where, every option with its GIVEN / WHEN / THEN, every vote with who cast it and why, the running tally, the cards it blocks, and the resolution once there is one. Use read_task_chat with the same id for the discussion thread.",
1322
+ fields: {
1323
+ decision: f.string({ min: 1, desc: "Decision slug or id" })
1324
+ }
1325
+ },
1326
+ mcp: {
1327
+ description: "Read one Conveyor decision in full \u2014 context, options, votes with reasons, tally, impacted cards, and the resolution. Pass projectId to disambiguate a slug; otherwise the configured default project is used.",
1328
+ fields: {
1329
+ projectId: mcpProjectId,
1330
+ decision: f.string({ desc: "Decision slug or id" })
1331
+ }
1332
+ }
1333
+ });
1334
+ var voteDecisionContract = defineToolContract({
1335
+ name: "vote_decision",
1336
+ agent: {
1337
+ description: "Vote on an open decision. One vote per voter: voting again replaces your previous one. Always give a reason \u2014 the resolution weighs reasons over counts, and a vote with no reason carries almost nothing.",
1338
+ fields: {
1339
+ decision: f.string({ min: 1, desc: "Decision slug or id" }),
1340
+ option: f.string({ min: 1, desc: "Option id, or its 1-based position" }),
1341
+ reason: f.optional(
1342
+ f.string({ desc: "Why this option, in one or two sentences (max 255 characters)" })
1343
+ )
1344
+ }
1345
+ },
1346
+ mcp: {
1347
+ description: "Vote on an open Conveyor decision, with a reason. One vote per voter \u2014 voting again replaces the previous one.",
1348
+ fields: {
1349
+ projectId: mcpProjectId,
1350
+ decision: f.string({ desc: "Decision slug or id" }),
1351
+ option: f.string({ desc: "Option id, or its 1-based position" }),
1352
+ reason: f.optional(f.string({ desc: "Why this option (max 255 characters)" }))
1353
+ }
1354
+ }
1355
+ });
1356
+ var resolveDecisionContract = defineToolContract({
1357
+ name: "resolve_decision",
1358
+ agent: {
1359
+ description: "Settle a decision. This is not a formality: the resolution is written into the plan of every card the decision blocked, into each of their chats, and into the overview of every tag it carries, and those cards become startable. Say what was decided in one or two sentences \u2014 a reader months later should not need to open the thread.",
1360
+ fields: {
1361
+ decision: f.string({ min: 1, desc: "Decision slug or id" }),
1362
+ option: f.string({ min: 1, desc: "Winning option id, or its 1-based position" }),
1363
+ resolution: f.string({
1364
+ min: 1,
1365
+ desc: "What was decided, in one or two plain sentences (max 255 characters)"
1366
+ }),
1367
+ notes: f.optional(
1368
+ f.string({ desc: "Markdown reasoning. Link any verdict attachment here." })
1369
+ )
1370
+ }
1371
+ },
1372
+ mcp: {
1373
+ description: "Settle a Conveyor decision. The resolution is written into every impacted card's plan and chat and into its tags' overviews, and the impacted cards become startable.",
1374
+ fields: {
1375
+ projectId: mcpProjectId,
1376
+ decision: f.string({ desc: "Decision slug or id" }),
1377
+ option: f.string({ desc: "Winning option id, or its 1-based position" }),
1378
+ resolution: f.string({ desc: "What was decided (max 255 characters)" }),
1379
+ notes: f.optional(f.string({ desc: "Markdown reasoning" }))
1380
+ }
1381
+ }
1382
+ });
1383
+ var updateDecisionContract = defineToolContract({
1384
+ name: "update_decision",
1385
+ agent: {
1386
+ description: "Change an open decision: fix the question, summary, context or tags, rewrite the options while nobody has voted, move the deadline, link or unlink the cards it blocks, or cancel it. Cancelling un-blocks the impacted cards without answering the question, so only do it when the question stopped mattering.",
1387
+ fields: {
1388
+ decision: f.string({ min: 1, desc: "Decision slug or id" }),
1389
+ question: f.optional(f.string({ min: 1, desc: "New question, one plain sentence" })),
1390
+ summary: f.optional(
1391
+ f.string({ desc: cardDescriptionDesc("New one- or two-sentence summary") })
1392
+ ),
1393
+ context: f.optional(f.string({ min: 1, desc: "New markdown background" })),
1394
+ options: f.optional(
1395
+ f.array(agentOption, {
1396
+ min: 2,
1397
+ desc: "Replacement options. Accepted only while the decision has no votes \u2014 once somebody has voted, cancel it and raise a new one instead. Send default_option with it: the old default no longer exists."
1398
+ })
1399
+ ),
1400
+ default_option: f.optional(
1401
+ f.number({ int: true, min: 1, max: 3, desc: "1-based position of the new default" })
1402
+ ),
1403
+ tag_names: f.optional(
1404
+ f.array(f.string(), { desc: "Replacement tag names (replaces the current set)" })
1405
+ ),
1406
+ deadline_hours: f.optional(f.number({ desc: "New deadline, in hours from now. 1 to 720." })),
1407
+ deadline_at: f.optional(
1408
+ f.string({
1409
+ desc: "New absolute deadline (ISO 8601). Wins over deadline_hours; must be in the future and within 720 hours."
1410
+ })
1411
+ ),
1412
+ add_impacted_cards: f.optional(
1413
+ f.array(f.string(), { desc: "Slugs or ids of cards to block on this decision" })
1414
+ ),
1415
+ remove_impacted_cards: f.optional(
1416
+ f.array(f.string(), { desc: "Ids of cards to stop blocking" })
1417
+ ),
1418
+ status: f.optional(
1419
+ f.enum(["Cancelled", "Open"], {
1420
+ desc: "Cancelled drops the question; Open reopens a settled one and clears its answer"
1421
+ })
1422
+ )
1423
+ }
1424
+ },
1425
+ mcp: {
1426
+ description: "Change an open Conveyor decision: fix the question, summary, context or tags, rewrite the options while nobody has voted, move the deadline, link or unlink impacted cards, cancel it, or reopen a settled one.",
1427
+ fields: {
1428
+ projectId: mcpProjectId,
1429
+ decision: f.string({ desc: "Decision slug or id" }),
1430
+ question: f.optional(f.string({ desc: "New question" })),
1431
+ summary: f.optional(f.string({ desc: cardDescriptionDesc("New summary") })),
1432
+ context: f.optional(f.string({ desc: "New markdown background" })),
1433
+ options: f.optional(
1434
+ f.array(agentOption, {
1435
+ min: 2,
1436
+ desc: "Replacement options. Accepted only while the decision has no votes. Send defaultOption with it: the old default no longer exists."
1437
+ })
1438
+ ),
1439
+ defaultOption: f.optional(
1440
+ f.number({ int: true, min: 1, max: 3, desc: "1-based position of the new default" })
1441
+ ),
1442
+ tagNames: f.optional(f.array(f.string(), { desc: "Replacement tag names" })),
1443
+ deadlineHours: f.optional(
1444
+ f.number({ min: 1, max: 720, desc: "New deadline, in hours from now" })
1445
+ ),
1446
+ deadlineAt: f.optional(
1447
+ f.string({ desc: "New absolute deadline (ISO 8601). Wins over deadlineHours." })
1448
+ ),
1449
+ addImpactedCards: f.optional(
1450
+ f.array(f.string(), { desc: "Slugs or ids of cards to block on this decision" })
1451
+ ),
1452
+ removeImpactedCards: f.optional(
1453
+ f.array(f.string(), { desc: "Ids of cards to stop blocking" })
1454
+ ),
1455
+ status: f.optional(
1456
+ f.enum(["Cancelled", "Open"], { desc: "Cancel it, or reopen a settled one" })
1457
+ )
1458
+ }
1459
+ }
1460
+ });
1461
+ var decisionsContracts = [
1462
+ createDecisionContract,
1463
+ listDecisionsContract,
1464
+ getDecisionContract,
1465
+ voteDecisionContract,
1466
+ resolveDecisionContract,
1467
+ updateDecisionContract
1468
+ ];
1172
1469
  var createPullRequestContract = defineToolContract({
1173
1470
  name: "create_pull_request",
1174
1471
  agent: {
@@ -1829,6 +2126,7 @@ var TOOL_CONTRACTS = Object.fromEntries(
1829
2126
  ...subtasksContracts,
1830
2127
  ...attachmentsContracts,
1831
2128
  ...suggestionsContracts,
2129
+ ...decisionsContracts,
1832
2130
  ...pullRequestContracts,
1833
2131
  ...integrationsContracts,
1834
2132
  ...driveContracts,
@@ -2146,13 +2444,22 @@ function registerProjectConfigTools(server2, conn2) {
2146
2444
  import { z as z6 } from "zod";
2147
2445
 
2148
2446
  // src/tools/tasks-format.ts
2447
+ function formatCompletedMeta(data) {
2448
+ const parts = [];
2449
+ const usage = data.usage;
2450
+ const total = typeof usage === "object" && usage !== null ? usage.totalTokens : void 0;
2451
+ if (typeof total === "number" && total > 0) parts.push(`tokens: ${total}`);
2452
+ if (typeof data.costUsd === "number") parts.push(`cost: $${data.costUsd}`);
2453
+ parts.push(`duration: ${typeof data.durationMs === "number" ? data.durationMs : "?"}ms`);
2454
+ return parts.join(", ");
2455
+ }
2149
2456
  var CLI_EVENT_FORMATTERS = {
2150
2457
  thinking: (data) => String(data.message ?? ""),
2151
2458
  tool_use: (data) => `${data.tool}: ${String(data.input ?? "").slice(0, 1e3)}`,
2152
2459
  tool_result: (data) => `${data.tool} \u2192 ${String(data.output ?? "").slice(0, 500)}${data.isError ? " [ERROR]" : ""}`,
2153
2460
  message: (data) => String(data.content ?? ""),
2154
2461
  error: (data) => `ERROR: ${String(data.message ?? "")}`,
2155
- completed: (data) => `Completed: ${data.summary ?? ""} (cost: $${data.costUsd ?? "?"}, duration: ${data.durationMs ?? "?"}ms)`,
2462
+ completed: (data) => `Completed: ${data.summary ?? ""} (${formatCompletedMeta(data)})`,
2156
2463
  setup_output: (data) => `[${data.stream ?? "stdout"}] ${String(data.data ?? "")}`,
2157
2464
  start_command_output: (data) => `[${data.stream ?? "stdout"}] ${String(data.data ?? "")}`,
2158
2465
  turn_end: (data) => `Turn complete (${Array.isArray(data.toolCalls) ? data.toolCalls.length : 0} tool calls)`
@@ -2176,7 +2483,7 @@ function compactChatMessage(message) {
2176
2483
  return obj;
2177
2484
  }
2178
2485
  function formatLegacyComputeSession(s) {
2179
- return ` - session ${s.id} [${s.provider}] pod=${s.instanceName ?? "-"} status=${s.status} runner=${s.agentRunnerStatus ?? "-"} lastHeartbeat=${s.lastHeartbeatAt ?? "-"} created=${s.createdAt} stopped=${s.stoppedAt ?? "-"}` + (s.deletionRequestedAt ? ` deletionRequested=${s.deletionRequestedAt} (attempts ${s.deletionAttempts})` : "") + (s.lastAgentEvent ? ` lastAgentEvent=${s.lastAgentEvent}` : "");
2486
+ return ` - session ${s.id} [${s.provider}] pod=${s.instanceName ?? "-"} status=${s.status} runner=${s.agentRunnerStatus ?? "-"} lastHeartbeat=${s.lastHeartbeatAt ?? "-"} created=${s.createdAt} stopped=${s.stoppedAt ?? "-"}` + (s.deletionRequestedAt ? ` deletionRequested=${s.deletionRequestedAt} (attempts ${s.deletionAttempts})` : "") + (s.lastAgentEvent ? ` lastAgentEvent=${s.lastAgentEvent}` : "") + (s.tokenUsage ? ` tokens=${s.tokenUsage.totalTokens} (${s.tokenUsage.turns} turns)` : "");
2180
2487
  }
2181
2488
  function formatWorkspaceSession(s) {
2182
2489
  return ` - workspaceSession ${s.id} role=${s.role} mode=${s.mode} status=${s.status} leaseUntil=${s.leaseUntil ?? "-"} created=${s.createdAt}`;
@@ -2241,7 +2548,7 @@ var STATUS_ENUM2 = [
2241
2548
  "Complete",
2242
2549
  "Cancelled"
2243
2550
  ];
2244
- var CARD_TYPE_ENUM = ["task", "incident", "suggestion"];
2551
+ var CARD_TYPE_ENUM = ["task", "incident", "suggestion", "decision"];
2245
2552
  var RISK_ENUM2 = ["critical", "high", "medium", "low"];
2246
2553
  var BOARD_FILTER = z6.string().nullable().optional().describe(
2247
2554
  "Filter to a sub-project board. Omit to use the connection's default board (CONVEYOR_SUBPROJECT_ID) when set, else the whole project; pass null to force the whole project. Use list_accessible_subprojects to find board IDs."
@@ -2253,7 +2560,7 @@ function registerListTasks(server2, conn2) {
2253
2560
  registerHotTool(
2254
2561
  server2,
2255
2562
  "list_tasks",
2256
- "List project cards, optionally filtered by card type, status, or assignment (a specific assignee, or unassigned tasks). Defaults to type=task \u2014 pass typeFilters to list incidents/suggestions. Results are relevance-ordered: highest priority first then newest; suggestions-only queries rank by upvote score. Pass projectId to target a specific project; otherwise the configured default project is used. Returns summaries \u2014 plan omitted, description truncated; use get_task for full details.",
2563
+ "List project cards, optionally filtered by card type, status, or assignment (a specific assignee, or unassigned tasks). Defaults to type=task \u2014 pass typeFilters to list incidents, suggestions or decisions. Results are relevance-ordered: highest priority first then newest; suggestions-only queries rank by upvote score. Pass projectId to target a specific project; otherwise the configured default project is used. Returns summaries \u2014 plan omitted, description truncated; use get_task for full details.",
2257
2564
  {
2258
2565
  projectId: z6.string().optional().describe("Target Conveyor project ID"),
2259
2566
  status: z6.enum(STATUS_ENUM2).optional().describe("Filter by task status"),
@@ -2799,10 +3106,11 @@ function registerUploadAttachment(server2, conn2) {
2799
3106
  ]
2800
3107
  };
2801
3108
  }
3109
+ const comment = params.comment ?? params.title;
2802
3110
  const result = await conn2.confirmFileUpload(
2803
3111
  params.taskId,
2804
3112
  fileId,
2805
- params.comment,
3113
+ comment,
2806
3114
  params.projectId,
2807
3115
  params.tags
2808
3116
  );
@@ -2811,6 +3119,11 @@ function registerUploadAttachment(server2, conn2) {
2811
3119
  ];
2812
3120
  if (result.downloadUrl) lines.push(`downloadUrl: ${result.downloadUrl}`);
2813
3121
  if (result.messageId) lines.push("Posted to the task chat.");
3122
+ else if (comment === void 0) {
3123
+ lines.push(
3124
+ "Not posted to the task chat \u2014 pass `comment` (or `title`) to show it in the card thread."
3125
+ );
3126
+ }
2814
3127
  if (result.appliedTags?.length) lines.push(`Tagged: ${result.appliedTags.join(", ")}.`);
2815
3128
  if (result.unknownTags?.length) {
2816
3129
  lines.push(
@@ -3312,6 +3625,8 @@ import { z as z72 } from "zod";
3312
3625
  import { z as z82 } from "zod";
3313
3626
  import { z as z92 } from "zod";
3314
3627
  import { z as z102 } from "zod";
3628
+ import { z as z11 } from "zod";
3629
+ import { z as z12 } from "zod";
3315
3630
  var ACHIEVEMENT_RARITIES = [
3316
3631
  {
3317
3632
  key: "common",
@@ -3395,6 +3710,13 @@ var TurnEndToolCallSchema = z22.object({
3395
3710
  output: z22.string().optional(),
3396
3711
  timestamp: z22.string().optional()
3397
3712
  }).passthrough();
3713
+ var AgentTurnUsageSchema = z22.object({
3714
+ inputTokens: z22.number(),
3715
+ outputTokens: z22.number(),
3716
+ cacheReadInputTokens: z22.number(),
3717
+ cacheCreationInputTokens: z22.number(),
3718
+ totalTokens: z22.number()
3719
+ });
3398
3720
  var KnownAgentEventSchema = z22.discriminatedUnion("type", [
3399
3721
  // ── Lifecycle / connection ────────────────────────────────────────────
3400
3722
  z22.object({
@@ -3434,7 +3756,11 @@ var KnownAgentEventSchema = z22.discriminatedUnion("type", [
3434
3756
  z22.object({
3435
3757
  type: z22.literal("completed"),
3436
3758
  summary: z22.string().optional(),
3437
- durationMs: z22.number().optional()
3759
+ durationMs: z22.number().optional(),
3760
+ /** Tokens this turn consumed, summed from the transcript's assistant
3761
+ * records. Optional: an older agent build sends none, and the server
3762
+ * then records nothing rather than zeroing the session's totals. */
3763
+ usage: AgentTurnUsageSchema.optional()
3438
3764
  }).passthrough(),
3439
3765
  z22.object({ type: z22.literal("error"), message: z22.string() }).passthrough(),
3440
3766
  z22.object({ type: z22.literal("agent_typing_start") }).passthrough(),
@@ -4800,135 +5126,304 @@ var DeleteTaskAuditRequestSchema = z92.object({
4800
5126
  var MarkInitialPromptSubmittedRequestSchema = z92.object({
4801
5127
  sessionId: z92.string()
4802
5128
  });
5129
+ var DECISION_STATUS_LABELS = ["Open", "Decided", "Cancelled"];
5130
+ var DECISION_OPTION_KINDS = ["current", "planned", "proposed"];
5131
+ var DECISION_VOTE_SOURCES = ["web", "slack", "discord", "agent"];
5132
+ var DECISION_OPTIONS_MIN = 2;
5133
+ var DECISION_OPTIONS_MAX = 3;
5134
+ var DECISION_OPTION_LABEL_MAX = 80;
5135
+ var DECISION_QUESTION_MAX = 200;
5136
+ var DECISION_RESOLUTION_MAX = CARD_DESCRIPTION_MAX;
5137
+ var DECISION_VOTE_REASON_MAX = CARD_DESCRIPTION_MAX;
5138
+ var DECISION_DEADLINE_MIN_HOURS = 1;
5139
+ var DECISION_DEADLINE_MAX_DAYS = 30;
5140
+ var DECISION_DEADLINE_MAX_HOURS = DECISION_DEADLINE_MAX_DAYS * 24;
5141
+ var DECISION_LIST_MAX_LIMIT = 200;
5142
+ var decisionIdSchema = z102.string().cuid("Invalid decision ID");
5143
+ var optionInputSchema = z102.object({
5144
+ label: z102.string().min(1, "Option label is required").max(DECISION_OPTION_LABEL_MAX),
5145
+ description: z102.string().min(1, "Describe the option as GIVEN / WHEN / THEN"),
5146
+ kind: z102.enum(DECISION_OPTION_KINDS).optional()
5147
+ });
5148
+ var optionsArraySchema = z102.array(optionInputSchema).min(DECISION_OPTIONS_MIN, `A decision needs at least ${DECISION_OPTIONS_MIN} options`).max(
5149
+ DECISION_OPTIONS_MAX,
5150
+ `A decision takes at most ${DECISION_OPTIONS_MAX} options \u2014 more than that means the question is not framed yet`
5151
+ );
5152
+ var CreateDecisionRequestSchema = z102.object({
5153
+ projectId: z102.string().cuid("Invalid project ID"),
5154
+ /** The question, as one plain sentence. */
5155
+ title: z102.string().min(1, "A decision needs a question").max(DECISION_QUESTION_MAX),
5156
+ description: z102.string().max(CARD_DESCRIPTION_MAX).optional(),
5157
+ context: z102.string().min(1, "Context is required \u2014 say why this is being asked now"),
5158
+ options: optionsArraySchema,
5159
+ /** 1-based position of the option that wins on silence. */
5160
+ defaultOptionOrdinal: z102.number().int().min(1).max(DECISION_OPTIONS_MAX),
5161
+ deadlineHours: z102.number().min(DECISION_DEADLINE_MIN_HOURS).max(DECISION_DEADLINE_MAX_HOURS).optional(),
5162
+ /** Cards this decision blocks, by cuid or slug. */
5163
+ impactedCardRefs: z102.array(z102.string().min(1)).max(50).optional(),
5164
+ tagNames: z102.array(z102.string().min(1)).max(10).optional(),
5165
+ raisedByTaskId: z102.string().min(1).optional(),
5166
+ raisedByPrNumber: z102.number().int().positive().optional()
5167
+ });
5168
+ var VoteDecisionRequestSchema = z102.object({
5169
+ decisionId: decisionIdSchema,
5170
+ optionId: z102.string().cuid("Invalid option ID"),
5171
+ reason: z102.string().max(DECISION_VOTE_REASON_MAX).optional(),
5172
+ source: z102.enum(DECISION_VOTE_SOURCES).optional()
5173
+ });
5174
+ var RemoveDecisionVoteRequestSchema = z102.object({
5175
+ decisionId: decisionIdSchema
5176
+ });
5177
+ var ResolveDecisionRequestSchema = z102.object({
5178
+ decisionId: decisionIdSchema,
5179
+ chosenOptionId: z102.string().cuid("Invalid option ID"),
5180
+ resolution: z102.string().min(1, "A resolution is required \u2014 say what was decided").max(DECISION_RESOLUTION_MAX),
5181
+ resolutionNotes: z102.string().optional()
5182
+ });
5183
+ var CancelDecisionRequestSchema = z102.object({
5184
+ decisionId: decisionIdSchema,
5185
+ /** Why it is being dropped. Posted to the decision thread. */
5186
+ reason: z102.string().max(DECISION_RESOLUTION_MAX).optional()
5187
+ });
5188
+ var ReopenDecisionRequestSchema = z102.object({
5189
+ decisionId: decisionIdSchema,
5190
+ deadlineHours: z102.number().min(DECISION_DEADLINE_MIN_HOURS).max(DECISION_DEADLINE_MAX_HOURS).optional()
5191
+ });
5192
+ var UpdateDecisionRequestSchema = z102.object({
5193
+ decisionId: decisionIdSchema,
5194
+ title: z102.string().min(1).max(200).optional(),
5195
+ description: z102.string().max(CARD_DESCRIPTION_MAX).optional(),
5196
+ context: z102.string().min(1).optional(),
5197
+ deadlineAt: z102.string().datetime().optional(),
5198
+ /** Only accepted while the decision has zero votes. */
5199
+ options: optionsArraySchema.optional(),
5200
+ defaultOptionOrdinal: z102.number().int().min(1).max(DECISION_OPTIONS_MAX).optional(),
5201
+ addImpactedCardRefs: z102.array(z102.string().min(1)).max(50).optional(),
5202
+ removeImpactedCardIds: z102.array(z102.string().min(1)).max(50).optional()
5203
+ });
5204
+ var GetDecisionRequestSchema = z102.object({
5205
+ /** cuid or slug. A slug needs `projectId` to resolve unambiguously. */
5206
+ decisionId: z102.string().min(1),
5207
+ projectId: z102.string().cuid().optional()
5208
+ });
5209
+ var ListDecisionsRequestSchema = z102.object({
5210
+ projectId: z102.string().cuid("Invalid project ID"),
5211
+ status: z102.enum(DECISION_STATUS_LABELS).optional(),
5212
+ /** Tag name, not id — an agent knows the tag it is working under. */
5213
+ tag: z102.string().min(1).optional(),
5214
+ /** Decisions that this card is blocked by (cuid or slug). */
5215
+ cardId: z102.string().min(1).optional(),
5216
+ query: z102.string().min(1).optional(),
5217
+ limit: z102.number().int().min(1).max(DECISION_LIST_MAX_LIMIT).optional()
5218
+ });
5219
+ var optionInput = z11.object({
5220
+ label: z11.string().min(1).max(DECISION_OPTION_LABEL_MAX),
5221
+ description: z11.string().min(1),
5222
+ kind: z11.enum(DECISION_OPTION_KINDS).optional()
5223
+ });
5224
+ var optionsInput = z11.array(optionInput).min(DECISION_OPTIONS_MIN).max(DECISION_OPTIONS_MAX);
5225
+ var deadlineHours = z11.number().min(DECISION_DEADLINE_MIN_HOURS).max(DECISION_DEADLINE_MAX_HOURS).optional();
5226
+ var CreateProjectDecisionRequestSchema = z11.object({
5227
+ projectId: z11.string(),
5228
+ question: z11.string().min(1).max(200),
5229
+ summary: z11.string().max(CARD_DESCRIPTION_MAX).optional(),
5230
+ context: z11.string().min(1),
5231
+ options: optionsInput,
5232
+ defaultOption: z11.number().int().min(1).max(DECISION_OPTIONS_MAX),
5233
+ deadlineHours,
5234
+ impactedCards: z11.array(z11.string().min(1)).max(50).optional(),
5235
+ /**
5236
+ * Pod surface only: also block the card this session is working on. The
5237
+ * session's task id is resolved SERVER-side from `sessionId` — a
5238
+ * client-supplied card id would let a pod block any card in the project.
5239
+ */
5240
+ includeCurrentCard: z11.boolean().optional(),
5241
+ sessionId: z11.string().optional(),
5242
+ tagNames: z11.array(z11.string().min(1)).max(10).optional(),
5243
+ raisedByPr: z11.number().int().positive().optional(),
5244
+ /** Slug or id of the card that raised the question. A link back, not a block. */
5245
+ raisedByCard: z11.string().min(1).optional(),
5246
+ /** Absolute deadline (ISO 8601). Wins over `deadlineHours` when both are sent. */
5247
+ deadlineAt: z11.string().datetime({ offset: true }).optional(),
5248
+ requestingUserId: z11.string().optional()
5249
+ });
5250
+ var ListProjectDecisionsRequestSchema = z11.object({
5251
+ projectId: z11.string(),
5252
+ status: z11.enum(DECISION_STATUS_LABELS).optional(),
5253
+ tag: z11.string().min(1).optional(),
5254
+ card: z11.string().min(1).optional(),
5255
+ query: z11.string().min(1).optional(),
5256
+ limit: z11.number().int().min(1).max(DECISION_LIST_MAX_LIMIT).optional(),
5257
+ requestingUserId: z11.string().optional()
5258
+ });
5259
+ var GetProjectDecisionRequestSchema = z11.object({
5260
+ projectId: z11.string(),
5261
+ decision: z11.string().min(1),
5262
+ requestingUserId: z11.string().optional()
5263
+ });
5264
+ var VoteProjectDecisionRequestSchema = z11.object({
5265
+ projectId: z11.string(),
5266
+ decision: z11.string().min(1),
5267
+ /** An option id, or its 1-based position. */
5268
+ option: z11.string().min(1),
5269
+ reason: z11.string().max(CARD_DESCRIPTION_MAX).optional(),
5270
+ requestingUserId: z11.string().optional()
5271
+ });
5272
+ var ResolveProjectDecisionRequestSchema = z11.object({
5273
+ projectId: z11.string(),
5274
+ decision: z11.string().min(1),
5275
+ option: z11.string().min(1),
5276
+ resolution: z11.string().min(1).max(DECISION_RESOLUTION_MAX),
5277
+ notes: z11.string().optional(),
5278
+ requestingUserId: z11.string().optional()
5279
+ });
5280
+ var UpdateProjectDecisionRequestSchema = z11.object({
5281
+ projectId: z11.string(),
5282
+ decision: z11.string().min(1),
5283
+ question: z11.string().min(1).max(200).optional(),
5284
+ summary: z11.string().max(CARD_DESCRIPTION_MAX).optional(),
5285
+ context: z11.string().min(1).optional(),
5286
+ /** Accepted only while nobody has voted. */
5287
+ options: optionsInput.optional(),
5288
+ defaultOption: z11.number().int().min(1).max(DECISION_OPTIONS_MAX).optional(),
5289
+ tagNames: z11.array(z11.string().min(1)).max(10).optional(),
5290
+ deadlineHours,
5291
+ /** Absolute deadline (ISO 8601). Wins over `deadlineHours` when both are sent. */
5292
+ deadlineAt: z11.string().datetime({ offset: true }).optional(),
5293
+ addImpactedCards: z11.array(z11.string().min(1)).max(50).optional(),
5294
+ removeImpactedCards: z11.array(z11.string().min(1)).max(50).optional(),
5295
+ status: z11.enum(["Cancelled", "Open"]).optional(),
5296
+ requestingUserId: z11.string().optional()
5297
+ });
4803
5298
  var MEETING_CHECKLIST_TITLE_MAX = 300;
4804
5299
  var MEETING_TRANSCRIPT_MAX_CHARS = 2e6;
4805
5300
  var MEETING_TITLE_MAX = 200;
4806
5301
  var MEETING_OCCURRED_AT_MIN_YEAR = 2e3;
4807
5302
  var MEETING_OCCURRED_AT_MAX_FUTURE_MS = 48 * 60 * 60 * 1e3;
4808
5303
  var OCCURRED_AT_RANGE_MESSAGE = `occurredAt must be a real date: no earlier than ${MEETING_OCCURRED_AT_MIN_YEAR}, and no more than 48 hours in the future.`;
4809
- var MeetingOccurredAtSchema = z102.string().datetime().refine((value) => {
5304
+ var MeetingOccurredAtSchema = z12.string().datetime().refine((value) => {
4810
5305
  const ms = Date.parse(value);
4811
5306
  if (Number.isNaN(ms)) return false;
4812
5307
  if (ms > Date.now() + MEETING_OCCURRED_AT_MAX_FUTURE_MS) return false;
4813
5308
  return new Date(ms).getUTCFullYear() >= MEETING_OCCURRED_AT_MIN_YEAR;
4814
5309
  }, OCCURRED_AT_RANGE_MESSAGE);
4815
- var CreateMeetingFromTranscriptRequestSchema = z102.object({
4816
- projectId: z102.string().cuid(),
4817
- rawText: z102.string().min(1).max(MEETING_TRANSCRIPT_MAX_CHARS),
4818
- title: z102.string().min(1).max(MEETING_TITLE_MAX).optional(),
5310
+ var CreateMeetingFromTranscriptRequestSchema = z12.object({
5311
+ projectId: z12.string().cuid(),
5312
+ rawText: z12.string().min(1).max(MEETING_TRANSCRIPT_MAX_CHARS),
5313
+ title: z12.string().min(1).max(MEETING_TITLE_MAX).optional(),
4819
5314
  /** ISO 8601. Defaults to now when the source carries no date. */
4820
5315
  occurredAt: MeetingOccurredAtSchema.optional(),
4821
5316
  /** Override auto-detection. Rarely needed; detection handles the three formats. */
4822
- format: z102.enum(["text", "vtt", "srt"]).optional(),
4823
- source: z102.enum(["manual", "slack"]).optional()
5317
+ format: z12.enum(["text", "vtt", "srt"]).optional(),
5318
+ source: z12.enum(["manual", "slack"]).optional()
4824
5319
  });
4825
- var GetMeetingRequestSchema = z102.object({
4826
- projectId: z102.string().cuid(),
4827
- meetingId: z102.string().cuid()
5320
+ var GetMeetingRequestSchema = z12.object({
5321
+ projectId: z12.string().cuid(),
5322
+ meetingId: z12.string().cuid()
4828
5323
  });
4829
- var UpdateMeetingRequestSchema = z102.object({
4830
- projectId: z102.string().cuid(),
4831
- meetingId: z102.string().cuid(),
4832
- title: z102.string().min(1).max(MEETING_TITLE_MAX).optional(),
5324
+ var UpdateMeetingRequestSchema = z12.object({
5325
+ projectId: z12.string().cuid(),
5326
+ meetingId: z12.string().cuid(),
5327
+ title: z12.string().min(1).max(MEETING_TITLE_MAX).optional(),
4833
5328
  occurredAt: MeetingOccurredAtSchema.optional()
4834
5329
  });
4835
- var RegenerateMeetingSummaryRequestSchema = z102.object({
4836
- projectId: z102.string().cuid(),
4837
- meetingId: z102.string().cuid()
5330
+ var RegenerateMeetingSummaryRequestSchema = z12.object({
5331
+ projectId: z12.string().cuid(),
5332
+ meetingId: z12.string().cuid()
4838
5333
  });
4839
- var DeleteMeetingRequestSchema = z102.object({
4840
- projectId: z102.string().cuid(),
4841
- meetingId: z102.string().cuid()
5334
+ var DeleteMeetingRequestSchema = z12.object({
5335
+ projectId: z12.string().cuid(),
5336
+ meetingId: z12.string().cuid()
4842
5337
  });
4843
- var checklistTitle = z102.string().min(1).max(MEETING_CHECKLIST_TITLE_MAX);
4844
- var ListMeetingChecklistRequestSchema = z102.object({
4845
- projectId: z102.string().cuid(),
4846
- meetingId: z102.string().cuid()
5338
+ var checklistTitle = z12.string().min(1).max(MEETING_CHECKLIST_TITLE_MAX);
5339
+ var ListMeetingChecklistRequestSchema = z12.object({
5340
+ projectId: z12.string().cuid(),
5341
+ meetingId: z12.string().cuid()
4847
5342
  });
4848
- var AddMeetingChecklistItemsRequestSchema = z102.object({
4849
- projectId: z102.string().cuid(),
4850
- meetingId: z102.string().cuid(),
4851
- items: z102.array(z102.object({ title: checklistTitle })).min(1).max(50)
5343
+ var AddMeetingChecklistItemsRequestSchema = z12.object({
5344
+ projectId: z12.string().cuid(),
5345
+ meetingId: z12.string().cuid(),
5346
+ items: z12.array(z12.object({ title: checklistTitle })).min(1).max(50)
4852
5347
  });
4853
- var UpdateMeetingChecklistItemRequestSchema = z102.object({
4854
- projectId: z102.string().cuid(),
4855
- meetingId: z102.string().cuid(),
4856
- itemId: z102.string().cuid(),
5348
+ var UpdateMeetingChecklistItemRequestSchema = z12.object({
5349
+ projectId: z12.string().cuid(),
5350
+ meetingId: z12.string().cuid(),
5351
+ itemId: z12.string().cuid(),
4857
5352
  title: checklistTitle.optional(),
4858
- ordinal: z102.number().int().min(0).optional(),
5353
+ ordinal: z12.number().int().min(0).optional(),
4859
5354
  /** Explicit null clears the link; undefined leaves it alone. */
4860
- linkedTaskId: z102.string().cuid().nullable().optional()
5355
+ linkedTaskId: z12.string().cuid().nullable().optional()
4861
5356
  }).refine(
4862
5357
  (v) => v.title !== void 0 || v.ordinal !== void 0 || v.linkedTaskId !== void 0,
4863
5358
  "Pass at least one of title, ordinal, or linkedTaskId."
4864
5359
  );
4865
- var DeleteMeetingChecklistItemRequestSchema = z102.object({
4866
- projectId: z102.string().cuid(),
4867
- meetingId: z102.string().cuid(),
4868
- itemId: z102.string().cuid()
4869
- });
4870
- var SetMeetingChecklistItemCheckedRequestSchema = z102.object({
4871
- projectId: z102.string().cuid(),
4872
- meetingId: z102.string().cuid(),
4873
- itemId: z102.string().cuid(),
4874
- checked: z102.boolean(),
5360
+ var DeleteMeetingChecklistItemRequestSchema = z12.object({
5361
+ projectId: z12.string().cuid(),
5362
+ meetingId: z12.string().cuid(),
5363
+ itemId: z12.string().cuid()
5364
+ });
5365
+ var SetMeetingChecklistItemCheckedRequestSchema = z12.object({
5366
+ projectId: z12.string().cuid(),
5367
+ meetingId: z12.string().cuid(),
5368
+ itemId: z12.string().cuid(),
5369
+ checked: z12.boolean(),
4875
5370
  /** Attach the card in the same call that ticks the item. */
4876
- linkedTaskId: z102.string().cuid().nullable().optional()
5371
+ linkedTaskId: z12.string().cuid().nullable().optional()
4877
5372
  });
4878
- var ListMeetingsRequestSchema = z102.object({
4879
- projectId: z102.string().cuid(),
4880
- limit: z102.number().int().min(1).max(50).optional(),
4881
- search: z102.string().max(200).optional()
5373
+ var ListMeetingsRequestSchema = z12.object({
5374
+ projectId: z12.string().cuid(),
5375
+ limit: z12.number().int().min(1).max(50).optional(),
5376
+ search: z12.string().max(200).optional()
4882
5377
  });
4883
- var ReadMeetingTranscriptRequestSchema = z102.object({
4884
- projectId: z102.string().cuid(),
4885
- meetingId: z102.string().cuid(),
4886
- offset: z102.number().int().min(0).optional(),
4887
- limit: z102.number().int().min(1).max(500).optional()
5378
+ var ReadMeetingTranscriptRequestSchema = z12.object({
5379
+ projectId: z12.string().cuid(),
5380
+ meetingId: z12.string().cuid(),
5381
+ offset: z12.number().int().min(0).optional(),
5382
+ limit: z12.number().int().min(1).max(500).optional()
4888
5383
  });
4889
5384
  var MEETING_SUMMARY_MAX_CHARS = 5e4;
4890
- var AddProjectMeetingChecklistItemsRequestSchema = z102.object({
4891
- projectId: z102.string().cuid(),
4892
- meetingId: z102.string().cuid(),
4893
- items: z102.array(z102.object({ title: z102.string().min(1).max(MEETING_CHECKLIST_TITLE_MAX) })).min(1).max(50),
4894
- requestingUserId: z102.string().optional()
4895
- });
4896
- var CheckProjectMeetingChecklistItemRequestSchema = z102.object({
4897
- projectId: z102.string().cuid(),
4898
- meetingId: z102.string().cuid(),
4899
- title: z102.string().min(1).max(MEETING_CHECKLIST_TITLE_MAX),
4900
- checked: z102.boolean(),
5385
+ var AddProjectMeetingChecklistItemsRequestSchema = z12.object({
5386
+ projectId: z12.string().cuid(),
5387
+ meetingId: z12.string().cuid(),
5388
+ items: z12.array(z12.object({ title: z12.string().min(1).max(MEETING_CHECKLIST_TITLE_MAX) })).min(1).max(50),
5389
+ requestingUserId: z12.string().optional()
5390
+ });
5391
+ var CheckProjectMeetingChecklistItemRequestSchema = z12.object({
5392
+ projectId: z12.string().cuid(),
5393
+ meetingId: z12.string().cuid(),
5394
+ title: z12.string().min(1).max(MEETING_CHECKLIST_TITLE_MAX),
5395
+ checked: z12.boolean(),
4901
5396
  /** Card id or slug. Resolved server-side and required to be in the project. */
4902
- linkedTask: z102.string().min(1).optional(),
4903
- requestingUserId: z102.string().optional()
4904
- });
4905
- var EditProjectMeetingChecklistItemRequestSchema = z102.object({
4906
- projectId: z102.string().cuid(),
4907
- meetingId: z102.string().cuid(),
4908
- title: z102.string().min(1).max(MEETING_CHECKLIST_TITLE_MAX),
4909
- newTitle: z102.string().min(1).max(MEETING_CHECKLIST_TITLE_MAX),
4910
- requestingUserId: z102.string().optional()
4911
- });
4912
- var RemoveProjectMeetingChecklistItemRequestSchema = z102.object({
4913
- projectId: z102.string().cuid(),
4914
- meetingId: z102.string().cuid(),
4915
- title: z102.string().min(1).max(MEETING_CHECKLIST_TITLE_MAX),
4916
- requestingUserId: z102.string().optional()
4917
- });
4918
- var CreateProjectMeetingRequestSchema = z102.object({
4919
- projectId: z102.string().cuid(),
4920
- rawText: z102.string().min(1).max(MEETING_TRANSCRIPT_MAX_CHARS),
4921
- title: z102.string().min(1).max(MEETING_TITLE_MAX).optional(),
5397
+ linkedTask: z12.string().min(1).optional(),
5398
+ requestingUserId: z12.string().optional()
5399
+ });
5400
+ var EditProjectMeetingChecklistItemRequestSchema = z12.object({
5401
+ projectId: z12.string().cuid(),
5402
+ meetingId: z12.string().cuid(),
5403
+ title: z12.string().min(1).max(MEETING_CHECKLIST_TITLE_MAX),
5404
+ newTitle: z12.string().min(1).max(MEETING_CHECKLIST_TITLE_MAX),
5405
+ requestingUserId: z12.string().optional()
5406
+ });
5407
+ var RemoveProjectMeetingChecklistItemRequestSchema = z12.object({
5408
+ projectId: z12.string().cuid(),
5409
+ meetingId: z12.string().cuid(),
5410
+ title: z12.string().min(1).max(MEETING_CHECKLIST_TITLE_MAX),
5411
+ requestingUserId: z12.string().optional()
5412
+ });
5413
+ var CreateProjectMeetingRequestSchema = z12.object({
5414
+ projectId: z12.string().cuid(),
5415
+ rawText: z12.string().min(1).max(MEETING_TRANSCRIPT_MAX_CHARS),
5416
+ title: z12.string().min(1).max(MEETING_TITLE_MAX).optional(),
4922
5417
  occurredAt: MeetingOccurredAtSchema.optional(),
4923
- requestingUserId: z102.string().optional()
5418
+ requestingUserId: z12.string().optional()
4924
5419
  });
4925
- var UpdateProjectMeetingRequestSchema = z102.object({
4926
- projectId: z102.string().cuid(),
4927
- meetingId: z102.string().cuid(),
4928
- title: z102.string().min(1).max(MEETING_TITLE_MAX).optional(),
5420
+ var UpdateProjectMeetingRequestSchema = z12.object({
5421
+ projectId: z12.string().cuid(),
5422
+ meetingId: z12.string().cuid(),
5423
+ title: z12.string().min(1).max(MEETING_TITLE_MAX).optional(),
4929
5424
  occurredAt: MeetingOccurredAtSchema.optional(),
4930
- summary: z102.string().min(1).max(MEETING_SUMMARY_MAX_CHARS).optional(),
4931
- requestingUserId: z102.string().optional()
5425
+ summary: z12.string().min(1).max(MEETING_SUMMARY_MAX_CHARS).optional(),
5426
+ requestingUserId: z12.string().optional()
4932
5427
  }).refine(
4933
5428
  (v) => v.title !== void 0 || v.occurredAt !== void 0 || v.summary !== void 0,
4934
5429
  "Pass at least one of title, occurredAt, or summary."
@@ -5265,13 +5760,17 @@ var CARD_TYPE_SURFACE = {
5265
5760
  task: "board",
5266
5761
  chat: "board",
5267
5762
  incident: "report",
5268
- suggestion: "report"
5763
+ suggestion: "report",
5764
+ // Decisions get their own surface: they are neither built on the board nor
5765
+ // triaged in the report feeds. Cards → Decisions is their only feed.
5766
+ decision: "decision"
5269
5767
  };
5270
5768
  var surfaceTypes = (surface) => Object.keys(CARD_TYPE_SURFACE).filter(
5271
5769
  (type) => CARD_TYPE_SURFACE[type] === surface
5272
5770
  );
5273
5771
  var BOARD_CARD_TYPES = surfaceTypes("board");
5274
5772
  var REPORT_CARD_TYPES = surfaceTypes("report");
5773
+ var DECISION_CARD_TYPES = surfaceTypes("decision");
5275
5774
 
5276
5775
  // src/tools/logs.ts
5277
5776
  function registerLogTools(server2, conn2) {
@@ -5452,6 +5951,78 @@ ${res.lines.join("\n")}${footer}` }] };
5452
5951
  });
5453
5952
  }
5454
5953
 
5954
+ // src/tools/decisions.ts
5955
+ function registerDecisionReads(server2, conn2) {
5956
+ registerContractTool(server2, listDecisionsContract, async (params) => {
5957
+ const res = await conn2.listDecisions(params);
5958
+ if (res.decisions.length === 0) {
5959
+ return {
5960
+ content: [
5961
+ {
5962
+ type: "text",
5963
+ text: "No decisions match. Nothing has been settled here, so the choice is open \u2014 state it as an assumption in the plan, or raise a decision with create_decision if the plan cannot be right without an answer."
5964
+ }
5965
+ ]
5966
+ };
5967
+ }
5968
+ return { content: [{ type: "text", text: JSON.stringify(res.decisions, null, 2) }] };
5969
+ });
5970
+ registerContractTool(server2, getDecisionContract, async (params) => {
5971
+ const res = await conn2.getDecision(params);
5972
+ return { content: [{ type: "text", text: JSON.stringify(res, null, 2) }] };
5973
+ });
5974
+ }
5975
+ function registerDecisionWrites(server2, conn2) {
5976
+ registerContractTool(server2, createDecisionContract, async (params) => {
5977
+ const res = await conn2.createDecision(params);
5978
+ const { impactedLinked, refused, ...detail } = res;
5979
+ const refusedLine = refused.length > 0 ? `
5980
+ Not linked: ${refused.map((r) => `${r.ref} (${r.reason})`).join("; ")}` : "";
5981
+ return {
5982
+ content: [
5983
+ {
5984
+ type: "text",
5985
+ text: `Raised decision ${res.slug} \u2014 ${res.url}
5986
+ Blocking ${impactedLinked.length} card(s).${refusedLine}
5987
+
5988
+ ${JSON.stringify(detail, null, 2)}`
5989
+ }
5990
+ ]
5991
+ };
5992
+ });
5993
+ registerContractTool(server2, voteDecisionContract, async (params) => {
5994
+ const res = await conn2.voteDecision(params);
5995
+ const tally = res.options.map((o) => `${o.label}: ${o.votes}`).join(", ");
5996
+ return { content: [{ type: "text", text: `Voted on ${res.slug}. Tally \u2014 ${tally}.` }] };
5997
+ });
5998
+ registerContractTool(server2, resolveDecisionContract, async (params) => {
5999
+ const res = await conn2.resolveDecision(params);
6000
+ return {
6001
+ content: [
6002
+ {
6003
+ type: "text",
6004
+ text: `Settled ${res.slug} as "${res.chosenOption}". ${res.impactedCount} card(s) were written back to and are no longer blocked.`
6005
+ }
6006
+ ]
6007
+ };
6008
+ });
6009
+ registerContractTool(server2, updateDecisionContract, async (params) => {
6010
+ const res = await conn2.updateDecision(params);
6011
+ return {
6012
+ content: [
6013
+ {
6014
+ type: "text",
6015
+ text: `Updated ${res.slug} \u2014 ${res.status}, ${res.impactedCount} impacted card(s), deadline ${res.deadlineAt}.`
6016
+ }
6017
+ ]
6018
+ };
6019
+ });
6020
+ }
6021
+ function registerDecisionTools(server2, conn2) {
6022
+ registerDecisionReads(server2, conn2);
6023
+ registerDecisionWrites(server2, conn2);
6024
+ }
6025
+
5455
6026
  // src/tools/index.ts
5456
6027
  function registerAllTools(server2, conn2) {
5457
6028
  registerProjectTools(server2, conn2);
@@ -5470,6 +6041,7 @@ function registerAllTools(server2, conn2) {
5470
6041
  registerIntegrationTools(server2, conn2);
5471
6042
  registerDriveTools(server2, conn2);
5472
6043
  registerMeetingTools(server2, conn2);
6044
+ registerDecisionTools(server2, conn2);
5473
6045
  }
5474
6046
 
5475
6047
  // src/cli.ts
@@ -1,5 +1,5 @@
1
1
  import { io, Socket } from 'socket.io-client';
2
- import { ListDriveFilesResponse, ReadDriveFileResponse, DriveFileDTO, DeleteDriveFileResponse, ProjectIntegrationsSummary, GaAnalyticsSummaryDTO, ProjectChannelDTO, ReadChannelMessagesResponse, PostChannelMessageResponse, ListMeetingsResponse, MeetingForAgent, ReadMeetingTranscriptResponse, MeetingChecklistItemForAgent } from '@project/shared';
2
+ import { ListDriveFilesResponse, ReadDriveFileResponse, DriveFileDTO, DeleteDriveFileResponse, ProjectIntegrationsSummary, GaAnalyticsSummaryDTO, ProjectChannelDTO, ReadChannelMessagesResponse, PostChannelMessageResponse, CreateProjectDecisionRequest, CreateProjectDecisionResponse, ListProjectDecisionsRequest, ProjectDecisionRow, ProjectDecisionDetail, VoteProjectDecisionRequest, ResolveProjectDecisionRequest, UpdateProjectDecisionRequest, ListMeetingsResponse, MeetingForAgent, ReadMeetingTranscriptResponse, MeetingChecklistItemForAgent } from '@project/shared';
3
3
 
4
4
  interface ConveyorMcpConfig {
5
5
  apiUrl: string;
@@ -585,6 +585,15 @@ declare class ConveyorConnection {
585
585
  lastHeartbeatAt: string | null;
586
586
  agentRunningAt: string | null;
587
587
  lastAgentEvent: string | null;
588
+ /** Tokens this session's turns consumed, or null when none recorded. */
589
+ tokenUsage: {
590
+ inputTokens: number;
591
+ outputTokens: number;
592
+ cacheReadInputTokens: number;
593
+ cacheCreationInputTokens: number;
594
+ totalTokens: number;
595
+ turns: number;
596
+ } | null;
588
597
  deletionRequestedAt: string | null;
589
598
  deletionAttempts: number;
590
599
  createdAt: string;
@@ -689,6 +698,27 @@ declare class ConveyorConnection {
689
698
  text: string;
690
699
  threadTs?: string;
691
700
  }): Promise<PostChannelMessageResponse>;
701
+ createDecision(params: Omit<CreateProjectDecisionRequest, "projectId"> & {
702
+ projectId?: string;
703
+ }): Promise<CreateProjectDecisionResponse>;
704
+ listDecisions(params: Omit<ListProjectDecisionsRequest, "projectId"> & {
705
+ projectId?: string;
706
+ }): Promise<{
707
+ decisions: ProjectDecisionRow[];
708
+ }>;
709
+ getDecision(params: {
710
+ projectId?: string;
711
+ decision: string;
712
+ }): Promise<ProjectDecisionDetail>;
713
+ voteDecision(params: Omit<VoteProjectDecisionRequest, "projectId"> & {
714
+ projectId?: string;
715
+ }): Promise<ProjectDecisionDetail>;
716
+ resolveDecision(params: Omit<ResolveProjectDecisionRequest, "projectId"> & {
717
+ projectId?: string;
718
+ }): Promise<ProjectDecisionDetail>;
719
+ updateDecision(params: Omit<UpdateProjectDecisionRequest, "projectId"> & {
720
+ projectId?: string;
721
+ }): Promise<ProjectDecisionDetail>;
692
722
  listMeetings(params: {
693
723
  projectId?: string;
694
724
  limit?: number;
package/dist/index.d.ts CHANGED
@@ -1,4 +1,4 @@
1
- export { A as ActivePtySession, d as ConveyorConnection, e as ConveyorMcpConfig, P as PtyAttachSnapshot, c as PtyDataChunk } from './connection-jZ8nSRwk.js';
1
+ export { A as ActivePtySession, d as ConveyorConnection, e as ConveyorMcpConfig, P as PtyAttachSnapshot, c as PtyDataChunk } from './connection-DeBZ_T6f.js';
2
2
  export { AttachTunnelOptions, ResolvedPtySession, RunTunnelOptions, TunnelConnection, TunnelHandle, TunnelSession, TunnelTty, WaitForPtySessionOptions, attachTunnel, runTunnel, waitForPtySession } from './tunnel.js';
3
3
  import 'socket.io-client';
4
4
  import '@project/shared';
package/dist/index.js CHANGED
@@ -5,7 +5,7 @@ import {
5
5
  } from "./chunk-HIVOGGE3.js";
6
6
  import {
7
7
  ConveyorConnection
8
- } from "./chunk-EUCQ26F7.js";
8
+ } from "./chunk-77XLXIFP.js";
9
9
  export {
10
10
  ConveyorConnection,
11
11
  attachTunnel,
@@ -7,7 +7,7 @@ import {
7
7
  } from "./chunk-5KU3QS6A.js";
8
8
  import {
9
9
  ConveyorConnection
10
- } from "./chunk-EUCQ26F7.js";
10
+ } from "./chunk-77XLXIFP.js";
11
11
 
12
12
  // src/tunnel-cli.ts
13
13
  var HELP = `conveyor-tunnel \u2014 attach your local terminal to a cloud Claude Code session.
package/dist/tunnel.d.ts CHANGED
@@ -1,4 +1,4 @@
1
- import { A as ActivePtySession, P as PtyAttachSnapshot, c as PtyDataChunk } from './connection-jZ8nSRwk.js';
1
+ import { A as ActivePtySession, P as PtyAttachSnapshot, c as PtyDataChunk } from './connection-DeBZ_T6f.js';
2
2
  import 'socket.io-client';
3
3
  import '@project/shared';
4
4
 
package/dist/wait-cli.js CHANGED
@@ -4,7 +4,7 @@ import {
4
4
  } from "./chunk-5KU3QS6A.js";
5
5
  import {
6
6
  ConveyorConnection
7
- } from "./chunk-EUCQ26F7.js";
7
+ } from "./chunk-77XLXIFP.js";
8
8
  import {
9
9
  runWait
10
10
  } from "./chunk-OPZL4NDT.js";
@@ -1,4 +1,4 @@
1
- import { C as CardCollectionPage, a as CardCollectionDelta } from './connection-jZ8nSRwk.js';
1
+ import { C as CardCollectionPage, a as CardCollectionDelta } from './connection-DeBZ_T6f.js';
2
2
  import { WaitFilter, WaitResult } from './wait.js';
3
3
  import 'socket.io-client';
4
4
  import '@project/shared';
package/dist/wait.d.ts CHANGED
@@ -1,4 +1,4 @@
1
- import { b as CardCollectionItem } from './connection-jZ8nSRwk.js';
1
+ import { b as CardCollectionItem } from './connection-DeBZ_T6f.js';
2
2
  import 'socket.io-client';
3
3
  import '@project/shared';
4
4
 
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@rallycry/conveyor-mcp",
3
- "version": "5.0.4",
3
+ "version": "5.0.6",
4
4
  "description": "Conveyor MCP server for Claude Code PM integration",
5
5
  "keywords": [
6
6
  "claude",