@rallycry/conveyor-mcp 5.0.16 → 5.0.18

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.
@@ -1,4 +1,4 @@
1
- // ../shared/dist/chunk-VSUBG7GR.js
1
+ // ../shared/dist/chunk-RSAY7FNH.js
2
2
  import { z } from "zod";
3
3
  import { z as z2 } from "zod";
4
4
  var DEFAULT_SONNET_MODEL = "claude-sonnet-5-5";
@@ -500,9 +500,9 @@ var searchFilterFields = {
500
500
  desc: "Only return cards with no assignee (mutually exclusive with assigneeId)"
501
501
  })
502
502
  ),
503
- limit: f.optional(f.number({ desc: "Max results to return (default 20)" }))
503
+ limit: f.optional(f.number({ desc: "Max results to return (default 20, at most 200)" }))
504
504
  };
505
- var searchResultNote = "Every result lists the tags it carries, so you can see why it matched. Results are relevance-ordered: highest priority first then newest; suggestions-only queries rank by upvote score. Returns summaries \u2014 plan omitted, description truncated; use get_task for full details.";
505
+ var searchResultNote = "Every result lists the tags it carries, so you can see why it matched. Searches every status, Complete and Cancelled included, unless statusFilters narrows it. Returns at most 200 results. Results are relevance-ordered: highest priority first then newest; suggestions-only queries rank by upvote score. Returns summaries \u2014 plan omitted, description truncated; use get_task for full details.";
506
506
  var searchTasksContract = defineToolContract({
507
507
  name: "search_tasks",
508
508
  agent: {
@@ -1096,7 +1096,7 @@ var listTaskFilesContract = defineToolContract({
1096
1096
  var getAttachmentContract = defineToolContract({
1097
1097
  name: "get_attachment",
1098
1098
  agent: {
1099
- description: "Fetch one task file's content plus metadata by file ID. This is the way to read attachment bytes: a document `downloadUrl` (anything that is not an image or video) answers 404 without a signed-in project member's session, so do not curl or WebFetch it from the pod. Call list_task_files first to discover IDs and check sizes \u2014 large binaries may be truncated by the service's size limit. Pass task_id when the file is attached to another card in this project, such as the pack parent.",
1099
+ description: "Fetch one task file by ID. Text and images under the inline size caps come back inline. Any other file (PDF, Word, Excel, zip, large logs) is saved into this pod, and the result names the local path: open it with the Read tool or Bash. A document downloadUrl answers 404 without a signed-in project member's session, so do not curl or WebFetch it. Call list_task_files first to discover IDs and sizes. Pass task_id when the file is attached to another card in this project, such as the pack parent.",
1100
1100
  fields: {
1101
1101
  fileId: f.string({ desc: "The file ID to retrieve" }),
1102
1102
  task_id: agentTaskIdOrSlug
@@ -4,10 +4,26 @@ import {
4
4
  } from "./chunk-D3Q2QZJA.js";
5
5
 
6
6
  // src/wait-runner.ts
7
+ var STATE_PAGE_LIMIT = 1e3;
8
+ var STATE_MAX_PAGES = 50;
7
9
  var SNAPSHOT_PAGE_LIMIT = 500;
8
10
  var SNAPSHOT_MAX_PAGES = 20;
9
11
  var RESYNC_ATTEMPTS = 4;
10
12
  var RESYNC_RETRY_DELAY_MS = 3e3;
13
+ function isLeanSeedUnsupported(error) {
14
+ const message = error instanceof Error ? error.message : String(error);
15
+ return /^Request timed out: listCardStates$|^Validation error|unknown method/i.test(message);
16
+ }
17
+ function toWaitCardFromState(row) {
18
+ return {
19
+ id: row.id,
20
+ slug: row.slug ?? null,
21
+ title: row.title ?? null,
22
+ type: row.type ?? null,
23
+ status: row.status ?? null,
24
+ assignedUserId: row.assignedUserId ?? null
25
+ };
26
+ }
11
27
  var WaitSession = class {
12
28
  constructor(options) {
13
29
  this.options = options;
@@ -23,6 +39,14 @@ var WaitSession = class {
23
39
  buffered = [];
24
40
  seeded = false;
25
41
  done = false;
42
+ /** Flips off once the API shows it lacks `listCardStates`. */
43
+ leanSeed = true;
44
+ /**
45
+ * Set once a `listCardStates` call has answered. From then on the method
46
+ * exists, so a failure is transient and goes to the resync retry — it never
47
+ * downgrades the session to the walk.
48
+ */
49
+ leanSeedConfirmed = false;
26
50
  offDelta;
27
51
  offReconnect;
28
52
  timer;
@@ -118,8 +142,57 @@ var WaitSession = class {
118
142
  break;
119
143
  }
120
144
  }
121
- /** Walk the scope to the end, or until the page cap stops us. */
145
+ /** The lean seed while the API has `listCardStates`, else the collection walk. */
122
146
  async snapshotAll() {
147
+ if (this.leanSeed) {
148
+ const lean = await this.leanSnapshot();
149
+ if (lean) return lean;
150
+ }
151
+ if (this.done) return { cards: [], complete: false, pages: 0 };
152
+ return this.walkCollection();
153
+ }
154
+ fetchStates(cursor) {
155
+ const { source, projectId, filter, statePageLimit = STATE_PAGE_LIMIT } = this.options;
156
+ return source.listCardStates(projectId, {
157
+ statuses: filter.statuses,
158
+ types: filter.types,
159
+ cursor,
160
+ limit: statePageLimit
161
+ });
162
+ }
163
+ /**
164
+ * Join the scope room, then page the cards in the watched statuses and
165
+ * types. Returns null when the first `listCardStates` call of the session
166
+ * fails the way an older API does: the session then walks the collection
167
+ * for the rest of its life.
168
+ */
169
+ async leanSnapshot() {
170
+ const { source, projectId, stateMaxPages = STATE_MAX_PAGES, log } = this.options;
171
+ await source.subscribeToCardCollection(projectId, { limit: 1 });
172
+ let page;
173
+ try {
174
+ page = await this.fetchStates(null);
175
+ } catch (error) {
176
+ if (this.leanSeedConfirmed || !isLeanSeedUnsupported(error)) throw error;
177
+ this.leanSeed = false;
178
+ if (!this.done) {
179
+ const message = error instanceof Error ? error.message : String(error);
180
+ log?.(`listCardStates did not answer (${message}) \u2014 walking the board instead.`);
181
+ }
182
+ return null;
183
+ }
184
+ this.leanSeedConfirmed = true;
185
+ const cards = page.items.map(toWaitCardFromState);
186
+ let pages = 1;
187
+ while (page.nextCursor !== null && pages < stateMaxPages) {
188
+ page = await this.fetchStates(page.nextCursor);
189
+ pages++;
190
+ cards.push(...page.items.map(toWaitCardFromState));
191
+ }
192
+ return { cards, complete: page.nextCursor === null, pages };
193
+ }
194
+ /** The fallback: walk the collection to the end, or until the page cap stops us. */
195
+ async walkCollection() {
123
196
  const {
124
197
  source,
125
198
  projectId,
@@ -129,11 +202,13 @@ var WaitSession = class {
129
202
  const cards = [];
130
203
  let cursor = null;
131
204
  let complete = false;
132
- for (let page = 0; page < maxPages; page++) {
205
+ let pages = 0;
206
+ while (pages < maxPages) {
133
207
  const result = await source.subscribeToCardCollection(projectId, {
134
208
  cursor,
135
209
  limit: pageLimit
136
210
  });
211
+ pages++;
137
212
  for (const item of result.items ?? []) cards.push(toWaitCard(item));
138
213
  cursor = result.nextCursor ?? null;
139
214
  if (cursor === null) {
@@ -141,16 +216,16 @@ var WaitSession = class {
141
216
  break;
142
217
  }
143
218
  }
144
- return { cards, complete };
219
+ return { cards, complete, pages };
145
220
  }
146
221
  async seedOrResync(initial) {
147
222
  this.seeded = false;
148
- const { cards, complete } = await this.snapshotAll();
223
+ const { cards, complete, pages } = await this.snapshotAll();
149
224
  if (this.done) return;
150
225
  if (initial) {
151
226
  this.tracker.seed(cards);
152
227
  this.options.log?.(
153
- `Watching ${cards.length} card(s) \u2014 ${this.tracker.size} already match; waiting for new work.`
228
+ `Seeded ${cards.length} card(s) from ${pages} page(s) \u2014 ${this.tracker.size} already match; waiting for new work.`
154
229
  );
155
230
  } else {
156
231
  const triggers = this.tracker.reseed(cards, complete);
@@ -394,7 +394,7 @@ var ConveyorConnection = class {
394
394
  });
395
395
  }
396
396
  resumeTask(taskId, projectId) {
397
- return this.callService("cloudBuildService", "wakeCodespace", {
397
+ return this.callService("cloudBuildService", "wakeWorkspace", {
398
398
  taskId,
399
399
  projectId: this.resolveProjectId(projectId)
400
400
  });
@@ -1104,6 +1104,20 @@ var ConveyorConnection = class {
1104
1104
  limit: opts.limit
1105
1105
  });
1106
1106
  }
1107
+ /**
1108
+ * A lean, status-filtered page of a project's cards, ordered by id — the
1109
+ * conveyor-wait seed. An API from before this method never acks the call,
1110
+ * so callers on an older server see a timeout (see `wait-runner.ts`).
1111
+ */
1112
+ listCardStates(projectId, opts) {
1113
+ return this.call("listCardStates", {
1114
+ projectId,
1115
+ statuses: opts.statuses,
1116
+ types: opts.types,
1117
+ cursor: opts.cursor ?? null,
1118
+ limit: opts.limit
1119
+ });
1120
+ }
1107
1121
  /**
1108
1122
  * Listen for card collection deltas on a project scope. The event name is
1109
1123
  * the room name by design — no id parsing client-side. Returns an
package/dist/cli.js CHANGED
@@ -81,13 +81,13 @@ import {
81
81
  uploadAttachmentContract,
82
82
  uploadSiteAssetsContract,
83
83
  voteDecisionContract
84
- } from "./chunk-P4WATE3Z.js";
84
+ } from "./chunk-BOAMGYLU.js";
85
85
  import {
86
86
  installLifecycle
87
87
  } from "./chunk-5KU3QS6A.js";
88
88
  import {
89
89
  ConveyorConnection
90
- } from "./chunk-E43OFCNP.js";
90
+ } from "./chunk-XLNIBIX4.js";
91
91
 
92
92
  // src/cli.ts
93
93
  import { createRequire } from "module";
@@ -119,7 +119,7 @@ function registerProjectTools(server2, conn2) {
119
119
  );
120
120
  server2.tool(
121
121
  "get_onboarding_status",
122
- "Get the project's onboarding/setup readiness checklist so you can drive setup to green without the user opening Project Settings. Returns { ok, checks[] } where each check has a key (githubApp, githubOauthScopes, claudeToken, devcontainer, devBranch, anthropicKey, compute, codespaceQuota), a status (ok | warn | fail), a human-readable reason, and an optional fix hint. Call this first during onboarding, resolve every 'fail' (use gh/gcloud CLIs or point the user at the fix), then call again until ok is true. Pass projectId to target a specific project; otherwise the configured default project is used.",
122
+ "Get the project's onboarding/setup readiness checklist so you can drive setup to green without the user opening Project Settings. Returns { ok, checks[] } where each check has a key (githubApp, githubOauthScopes, codingAgentCredential, devBranch, anthropicKey, compute), a status (ok | warn | fail), a human-readable reason, and an optional fix hint. Call this first during onboarding, resolve every 'fail' (use gh/gcloud CLIs or point the user at the fix), then call again until ok is true. Pass projectId to target a specific project; otherwise the configured default project is used.",
123
123
  {
124
124
  projectId: z.string().optional().describe("Target Conveyor project ID")
125
125
  },
@@ -536,7 +536,7 @@ function formatTaskSessionState(t, limit = 20) {
536
536
  }
537
537
  }
538
538
  if (t.workspaces.length === 0) {
539
- lines.push(" (no v3 workspaces)");
539
+ lines.push(" (no workspaces)");
540
540
  } else {
541
541
  const { kept, omitted } = latestByCreatedAt(t.workspaces, limit);
542
542
  lines.push(...kept.flatMap(formatWorkspace));
@@ -552,7 +552,7 @@ function summarizeTask(task) {
552
552
  const { plan, description, ...rest } = task;
553
553
  const summary = { ...rest };
554
554
  summary.description = typeof description === "string" && description.length > DESCRIPTION_PREVIEW_CHARS ? `${description.slice(0, DESCRIPTION_PREVIEW_CHARS)}\u2026 [truncated \u2014 use get_task for the full description]` : description ?? null;
555
- summary.hasPlan = typeof plan === "string" && plan.length > 0;
555
+ summary.hasPlan = typeof plan === "string" ? plan.length > 0 : rest.hasPlan === true;
556
556
  return summary;
557
557
  }
558
558
 
@@ -579,10 +579,10 @@ function registerListTasks(server2, conn2) {
579
579
  registerHotTool(
580
580
  server2,
581
581
  "list_tasks",
582
- "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.",
582
+ "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. Without a status, Complete and Cancelled cards are left out \u2014 pass status to list one of them, or use search_tasks, which covers every status. Returns at most 200 cards. 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.",
583
583
  {
584
584
  projectId: z5.string().optional().describe("Target Conveyor project ID"),
585
- status: z5.enum(STATUS_ENUM).optional().describe("Filter by task status"),
585
+ status: z5.enum(STATUS_ENUM).optional().describe("Filter by task status. Omit for every status except Complete and Cancelled."),
586
586
  typeFilters: z5.array(z5.enum(CARD_TYPE_ENUM)).optional().describe(
587
587
  'Card types to include, e.g. ["incident"] or ["task", "incident"]. Omit for tasks only.'
588
588
  ),
@@ -590,7 +590,7 @@ function registerListTasks(server2, conn2) {
590
590
  unassigned: z5.boolean().optional().describe("Only return tasks with no assignee (mutually exclusive with assigneeId)"),
591
591
  subProjectId: BOARD_FILTER,
592
592
  onHold: z5.boolean().optional().describe("true = only cards on hold, false = exclude them, omit = both"),
593
- limit: z5.number().optional().describe("Max tasks to return (default 50)")
593
+ limit: z5.number().optional().describe("Max tasks to return (default 50, at most 200)")
594
594
  },
595
595
  async (params) => {
596
596
  const tasks = await conn2.listTasks(params);
@@ -770,7 +770,7 @@ function registerGetTaskCli(server2, conn2) {
770
770
  function registerGetTaskSessions(server2, conn2) {
771
771
  server2.tool(
772
772
  "get_task_sessions",
773
- "Read compute-session state for a task: legacy CodespaceSession rows plus v3 workspaces, including purpose=review review workspaces. Shows pod identity, liveness, lifecycle, and code-review claim state. Use to diagnose stalled/dead agents or code-review runs. Pass projectId to target a specific project; otherwise the configured default project is used.",
773
+ "Read compute-session state for a task: workspace sessions and workspaces, including purpose=review review workspaces. Shows pod identity, liveness, lifecycle, and code-review claim state. Use to diagnose stalled/dead agents or code-review runs. Pass projectId to target a specific project; otherwise the configured default project is used.",
774
774
  {
775
775
  projectId: z5.string().optional().describe("Target Conveyor project ID"),
776
776
  taskId: z5.string().describe("The task ID or slug"),
@@ -958,7 +958,7 @@ function registerTaskLifecycleTools(server2, conn2) {
958
958
  function registerBuildTools(server2, conn2) {
959
959
  server2.tool(
960
960
  "start_task",
961
- "Start a cloud build (codespace) for a task. Pass projectId to target a specific project; otherwise the configured default project is used.",
961
+ "Start a cloud build for a task. Pass projectId to target a specific project; otherwise the configured default project is used.",
962
962
  {
963
963
  projectId: z6.string().optional().describe("Target Conveyor project ID"),
964
964
  taskId: z6.string().describe("The task ID")
@@ -997,7 +997,7 @@ function registerBuildTools(server2, conn2) {
997
997
  );
998
998
  server2.tool(
999
999
  "get_build_status",
1000
- "Check codespace and agent status for a task. Pass projectId to target a specific project; otherwise the configured default project is used.",
1000
+ "Check environment and agent status for a task. Pass projectId to target a specific project; otherwise the configured default project is used.",
1001
1001
  {
1002
1002
  projectId: z6.string().optional().describe("Target Conveyor project ID"),
1003
1003
  taskId: z6.string().describe("The task ID")
@@ -1767,7 +1767,9 @@ var GetTaskFilesRequestSchema = z52.object({
1767
1767
  var GetTaskFileRequestSchema = z52.object({
1768
1768
  sessionId: z52.string(),
1769
1769
  fileId: z52.string(),
1770
- taskId: z52.string().optional()
1770
+ taskId: z52.string().optional(),
1771
+ /** Ask for a short-lived `agentDownloadUrl` when the bytes are not inlined. */
1772
+ agentDownload: z52.boolean().optional()
1771
1773
  });
1772
1774
  var DeleteTaskFileRequestSchema = z52.object({
1773
1775
  sessionId: z52.string(),
@@ -1907,10 +1909,6 @@ var ReportDiscoveredPortsRequestSchema = z52.object({
1907
1909
  sessionId: z52.string(),
1908
1910
  ports: z52.array(DiscoveredPortSchema).max(64)
1909
1911
  });
1910
- var ReportBootMilestoneRequestSchema = z52.object({
1911
- sessionId: z52.string(),
1912
- key: z52.string().max(64)
1913
- });
1914
1912
  var CreateSubtaskRequestSchema = z52.object({
1915
1913
  sessionId: z52.string(),
1916
1914
  title: z52.string().min(1),
@@ -2274,6 +2272,13 @@ var ListProjectTasksRequestSchema = z72.object({
2274
2272
  }).refine((p) => !(p.unassigned && p.assigneeId), {
2275
2273
  message: "Pass either assigneeId or unassigned, not both"
2276
2274
  });
2275
+ var ListCardStatesRequestSchema = z72.object({
2276
+ projectId: z72.string(),
2277
+ statuses: z72.array(z72.string()).min(1).max(32),
2278
+ types: z72.array(z72.string()).optional(),
2279
+ cursor: z72.string().nullable().optional(),
2280
+ limit: z72.number().int().positive().optional().default(500)
2281
+ });
2277
2282
  var GetProjectTaskRequestSchema = z72.object({
2278
2283
  projectId: z72.string(),
2279
2284
  taskId: z72.string()
@@ -3395,6 +3400,8 @@ var ANTHROPIC_CATALOG = [
3395
3400
  anthropicEntry(DEFAULT_HAIKU_MODEL, "Haiku 4.5", 1, 5, { supportsEffort: false }),
3396
3401
  anthropicEntry(FABLE_MODEL, "Fable 5.1", 10, 50)
3397
3402
  ];
3403
+ var PROJECT_ARCHIVE_RETENTION_DAYS = 30;
3404
+ var RETENTION_MS = PROJECT_ARCHIVE_RETENTION_DAYS * 24 * 60 * 60 * 1e3;
3398
3405
  var CLAUDESPACE_WORKLOAD_LABEL = "rc-workload";
3399
3406
  var CLAUDESPACE_WORKLOAD_VALUE = "claudespace";
3400
3407
  var CONVEYOR_POD_SELECTOR = `${CLAUDESPACE_WORKLOAD_LABEL}=${CLAUDESPACE_WORKLOAD_VALUE}`;
@@ -3994,7 +4001,7 @@ function registerStartTunnelTool(server2, conn2, startTunnel) {
3994
4001
  const info = await conn2.getWorkspaceAttachInfo(taskId, sshPublicKey);
3995
4002
  if (!info.tunnelUrl || !info.attachToken) {
3996
4003
  throw new Error(
3997
- "Workspace tunnels are unavailable for this task (GitHub Codespace-backed environments expose previews only \u2014 use workspace_preview_urls)."
4004
+ "Workspace tunnels are unavailable for this task \u2014 it has no live workspace tunnel. Use workspace_preview_urls for previews."
3998
4005
  );
3999
4006
  }
4000
4007
  const remotePort = port ?? info.ssh?.remotePort ?? 2222;
@@ -130,6 +130,23 @@ interface CardCollectionPage {
130
130
  nextCursor: string | null;
131
131
  totalCount: number;
132
132
  }
133
+ /**
134
+ * One `listCardStates` row: the six fields conveyor-wait filters on and
135
+ * prints, with a flat assignee id. Mirrors `CardStateDTO` in @project/shared.
136
+ */
137
+ interface CardStateRow {
138
+ id: string;
139
+ slug: string | null;
140
+ title: string | null;
141
+ type: string | null;
142
+ status: string | null;
143
+ assignedUserId: string | null;
144
+ }
145
+ /** One page of `listCardStates`, ordered by id. */
146
+ interface CardStatePage {
147
+ items: CardStateRow[];
148
+ nextCursor: string | null;
149
+ }
133
150
  /**
134
151
  * A quickdraw collection change on a card scope. `added` vs `updated` is
135
152
  * cosmetic — most card writes go through a hand-rolled upsert that always
@@ -164,9 +181,6 @@ interface WorkspaceAttachInfo {
164
181
  taskId: string;
165
182
  sessionId: string;
166
183
  workspaceRoot: string;
167
- /** "codespace" means preview-only — GitHub owns the VM's forwarded ports, so
168
- * `tunnelUrl`/`attachToken`/`ssh` are null/absent. Older servers omit it. */
169
- backend?: "gke" | "codespace";
170
184
  tunnelUrl: string | null;
171
185
  attachToken: string | null;
172
186
  expiresAt: string;
@@ -533,7 +547,8 @@ declare class ConveyorConnection {
533
547
  }>;
534
548
  sleepTask(taskId: string, projectId?: string): Promise<{
535
549
  taskId: string;
536
- codespaceStatus: string;
550
+ codespaceStatus?: string;
551
+ workspaceStatus?: string;
537
552
  }>;
538
553
  resumeTask(taskId: string, projectId?: string): Promise<{
539
554
  status: string;
@@ -1101,6 +1116,17 @@ declare class ConveyorConnection {
1101
1116
  cursor?: string | null;
1102
1117
  limit?: number;
1103
1118
  }): Promise<CardCollectionPage>;
1119
+ /**
1120
+ * A lean, status-filtered page of a project's cards, ordered by id — the
1121
+ * conveyor-wait seed. An API from before this method never acks the call,
1122
+ * so callers on an older server see a timeout (see `wait-runner.ts`).
1123
+ */
1124
+ listCardStates(projectId: string, opts: {
1125
+ statuses: string[];
1126
+ types?: string[];
1127
+ cursor?: string | null;
1128
+ limit?: number;
1129
+ }): Promise<CardStatePage>;
1104
1130
  /**
1105
1131
  * Listen for card collection deltas on a project scope. The event name is
1106
1132
  * the room name by design — no id parsing client-side. Returns an
@@ -1117,4 +1143,4 @@ declare class ConveyorConnection {
1117
1143
  disconnect(): void;
1118
1144
  }
1119
1145
 
1120
- export { type ActivePtySession as A, type CardCollectionPage as C, type PtyAttachSnapshot as P, type CardCollectionDelta as a, type CardCollectionItem as b, type PtyDataChunk as c, ConveyorConnection as d, type ConveyorMcpConfig as e };
1146
+ export { type ActivePtySession as A, type CardCollectionPage as C, type PtyAttachSnapshot as P, type CardStatePage as a, type CardCollectionDelta as b, type CardCollectionItem as c, type PtyDataChunk as d, ConveyorConnection as e, type ConveyorMcpConfig as f };
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-aXGLRE5V.js';
1
+ export { A as ActivePtySession, e as ConveyorConnection, f as ConveyorMcpConfig, P as PtyAttachSnapshot, d as PtyDataChunk } from './connection-C2qTFync.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-E43OFCNP.js";
8
+ } from "./chunk-XLNIBIX4.js";
9
9
  export {
10
10
  ConveyorConnection,
11
11
  attachTunnel,
@@ -1,10 +1,10 @@
1
1
  #!/usr/bin/env node
2
2
  import {
3
3
  transferSiteAsset
4
- } from "./chunk-P4WATE3Z.js";
4
+ } from "./chunk-BOAMGYLU.js";
5
5
  import {
6
6
  ConveyorConnection
7
- } from "./chunk-E43OFCNP.js";
7
+ } from "./chunk-XLNIBIX4.js";
8
8
 
9
9
  // src/site-assets-cli.ts
10
10
  import crypto from "crypto";
@@ -7,7 +7,7 @@ import {
7
7
  } from "./chunk-5KU3QS6A.js";
8
8
  import {
9
9
  ConveyorConnection
10
- } from "./chunk-E43OFCNP.js";
10
+ } from "./chunk-XLNIBIX4.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-aXGLRE5V.js';
1
+ import { A as ActivePtySession, P as PtyAttachSnapshot, d as PtyDataChunk } from './connection-C2qTFync.js';
2
2
  import 'socket.io-client';
3
3
  import '@project/shared';
4
4
 
package/dist/wait-cli.js CHANGED
@@ -4,10 +4,10 @@ import {
4
4
  } from "./chunk-5KU3QS6A.js";
5
5
  import {
6
6
  ConveyorConnection
7
- } from "./chunk-E43OFCNP.js";
7
+ } from "./chunk-XLNIBIX4.js";
8
8
  import {
9
9
  runWait
10
- } from "./chunk-OPZL4NDT.js";
10
+ } from "./chunk-J7LQ5DAT.js";
11
11
  import {
12
12
  DEFAULT_WAIT_SCOPES,
13
13
  DEFAULT_WAIT_STATUSES,
@@ -1,4 +1,4 @@
1
- import { C as CardCollectionPage, a as CardCollectionDelta } from './connection-aXGLRE5V.js';
1
+ import { C as CardCollectionPage, a as CardStatePage, b as CardCollectionDelta } from './connection-C2qTFync.js';
2
2
  import { WaitFilter, WaitResult } from './wait.js';
3
3
  import 'socket.io-client';
4
4
  import '@project/shared';
@@ -7,9 +7,16 @@ import '@project/shared';
7
7
  * `conveyor-wait` runner — the live half of the engine.
8
8
  *
9
9
  * `wait.ts` decides what "matching" means; this module owns the socket-shaped
10
- * lifecycle around it: seed from a paged snapshot, apply deltas, re-snapshot
11
- * after a `reset` or a reconnect, and settle on the first card that enters the
12
- * filter (or on the deadline).
10
+ * lifecycle around it: seed, apply deltas, re-seed after a `reset` or a
11
+ * reconnect, and settle on the first card that enters the filter (or on the
12
+ * deadline).
13
+ *
14
+ * A seed is two calls, not a walk of the board. A cursor-less collection
15
+ * subscribe with `limit: 1` joins the scope room, which is what makes deltas
16
+ * flow; its one item is ignored. `listCardStates` then pages just the cards in
17
+ * the watched statuses and types — six fields each, usually one page. An API
18
+ * from before `listCardStates` gets today's walk of the collection instead
19
+ * (conveyor-mcp publishes ahead of the API release).
13
20
  *
14
21
  * It talks to a narrow `CardCollectionSource` rather than a socket, so the
15
22
  * reconnect, reset, and paging paths are unit-testable without a server.
@@ -24,6 +31,12 @@ interface CardCollectionSource {
24
31
  cursor?: string | null;
25
32
  limit?: number;
26
33
  }): Promise<CardCollectionPage>;
34
+ listCardStates(projectId: string, opts: {
35
+ statuses: string[];
36
+ types?: string[];
37
+ cursor?: string | null;
38
+ limit?: number;
39
+ }): Promise<CardStatePage>;
27
40
  onCardDelta(projectId: string, handler: (delta: CardCollectionDelta) => void): () => void;
28
41
  onReconnect(handler: () => void): () => void;
29
42
  }
@@ -32,7 +45,13 @@ interface RunWaitOptions {
32
45
  projectId: string;
33
46
  filter: WaitFilter;
34
47
  timeoutMs: number;
48
+ /** Rows per `listCardStates` page. */
49
+ statePageLimit?: number;
50
+ /** Page cap for one `listCardStates` seed. */
51
+ stateMaxPages?: number;
52
+ /** Items per page of the fallback collection walk. */
35
53
  pageLimit?: number;
54
+ /** Page cap for the fallback collection walk. */
36
55
  maxPages?: number;
37
56
  /** Backoff between re-snapshot attempts. Overridable so tests stay fast. */
38
57
  resyncRetryDelayMs?: number;
@@ -1,6 +1,6 @@
1
1
  import {
2
2
  runWait
3
- } from "./chunk-OPZL4NDT.js";
3
+ } from "./chunk-J7LQ5DAT.js";
4
4
  import "./chunk-D3Q2QZJA.js";
5
5
  export {
6
6
  runWait
package/dist/wait.d.ts CHANGED
@@ -1,4 +1,4 @@
1
- import { b as CardCollectionItem } from './connection-aXGLRE5V.js';
1
+ import { c as CardCollectionItem } from './connection-C2qTFync.js';
2
2
  import 'socket.io-client';
3
3
  import '@project/shared';
4
4
 
@@ -14,10 +14,14 @@ import '@project/shared';
14
14
  * - **Creates arrive as `updated` deltas.** Most card writes go through the
15
15
  * service's hand-rolled upsert choke point, which always emits `updated`.
16
16
  * "New work" is therefore a set diff, never a delta-type check.
17
- * - **The snapshot ack is one PAGE, not the scope.** `cardsByProject` omits
18
- * `ids` (the scope is unbounded), so seeding walks `nextCursor` to the end.
19
- * When the walk is capped we merge instead of replacing, because dropping an
20
- * unseen id would make it look newly-matching on the next delta.
17
+ * - **The seed is not the collection snapshot.** A snapshot ack is one page
18
+ * of full cards, and a large project's scope runs to thousands of them. The
19
+ * seed instead pages `listCardStates`: only the watched statuses and types,
20
+ * six fields a row. The collection subscribe (with `limit: 1`) only joins
21
+ * the delta room. An older API without `listCardStates` falls back to
22
+ * walking the collection's `nextCursor` to the end. When either walk is
23
+ * capped we merge instead of replacing, because dropping an unseen id would
24
+ * make it look newly-matching on the next delta.
21
25
  *
22
26
  * The live half — snapshot paging, reconnect, and settling — lives in
23
27
  * `wait-runner.ts`; this module is the pure filter and set-diff logic it uses.
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@rallycry/conveyor-mcp",
3
- "version": "5.0.16",
3
+ "version": "5.0.18",
4
4
  "description": "Conveyor MCP server for Claude Code PM integration",
5
5
  "keywords": [
6
6
  "claude",
@@ -36,7 +36,7 @@
36
36
  "test:unit": "vitest run --passWithNoTests"
37
37
  },
38
38
  "dependencies": {
39
- "@modelcontextprotocol/sdk": "^1.30.1",
39
+ "@modelcontextprotocol/sdk": "^1.32.1",
40
40
  "socket.io-client": "^4.8.4",
41
41
  "ws": "8.22.0",
42
42
  "zod": "^4.6.5"