@rallycry/conveyor-mcp 3.4.0 → 3.6.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.
package/dist/tunnel.d.ts CHANGED
@@ -1,7 +1,7 @@
1
1
  interface ConveyorMcpConfig {
2
2
  apiUrl: string;
3
3
  projectToken: string;
4
- projectId: string;
4
+ projectId?: string;
5
5
  }
6
6
  /** A single PTY output frame broadcast on the `pty:data` room event. */
7
7
  interface PtyDataChunk {
@@ -54,6 +54,8 @@ declare class ConveyorConnection {
54
54
  private config;
55
55
  constructor(config: ConveyorMcpConfig);
56
56
  get projectId(): string;
57
+ private resolveProjectId;
58
+ private normalizeProjectList;
57
59
  connect(): Promise<void>;
58
60
  private call;
59
61
  /**
@@ -66,13 +68,16 @@ declare class ConveyorConnection {
66
68
  private emit;
67
69
  private callService;
68
70
  listTasks(params: {
71
+ projectId?: string;
69
72
  status?: string;
70
73
  assigneeId?: string;
71
74
  unassigned?: boolean;
72
75
  limit?: number;
73
76
  }): Promise<unknown[]>;
74
- getTask(taskId: string): Promise<unknown>;
77
+ getTask(taskId: string, projectId?: string): Promise<unknown>;
78
+ getCardBySlug(slug: string, projectId?: string): Promise<unknown>;
75
79
  searchTasks(params: {
80
+ projectId?: string;
76
81
  tagNames?: string[];
77
82
  searchQuery?: string;
78
83
  statusFilters?: string[];
@@ -81,6 +86,7 @@ declare class ConveyorConnection {
81
86
  limit?: number;
82
87
  }): Promise<unknown[]>;
83
88
  createTask(params: {
89
+ projectId?: string;
84
90
  title: string;
85
91
  description?: string;
86
92
  plan?: string;
@@ -90,6 +96,7 @@ declare class ConveyorConnection {
90
96
  slug: string;
91
97
  }>;
92
98
  updateTask(params: {
99
+ projectId?: string;
93
100
  taskId: string;
94
101
  title?: string;
95
102
  description?: string;
@@ -101,6 +108,7 @@ declare class ConveyorConnection {
101
108
  status: string;
102
109
  }>;
103
110
  addReviewer(params: {
111
+ projectId?: string;
104
112
  taskId: string;
105
113
  userId: string;
106
114
  }): Promise<{
@@ -111,6 +119,7 @@ declare class ConveyorConnection {
111
119
  }>;
112
120
  }>;
113
121
  removeReviewer(params: {
122
+ projectId?: string;
114
123
  taskId: string;
115
124
  userId: string;
116
125
  }): Promise<{
@@ -120,75 +129,79 @@ declare class ConveyorConnection {
120
129
  name: string | null;
121
130
  }>;
122
131
  }>;
123
- listProjectMembers(): Promise<Array<{
132
+ listProjectMembers(projectId?: string): Promise<Array<{
124
133
  userId: string;
125
134
  name: string | null;
126
135
  email: string;
127
136
  level: string;
128
137
  }>>;
129
- startBuild(taskId: string): Promise<{
138
+ listProjects(): Promise<unknown[]>;
139
+ startBuild(taskId: string, projectId?: string): Promise<{
130
140
  taskId: string;
131
141
  status: string;
132
142
  }>;
133
- stopBuild(taskId: string): Promise<{
143
+ stopBuild(taskId: string, projectId?: string): Promise<{
134
144
  taskId: string;
135
145
  stopped: boolean;
136
146
  }>;
137
- getBuildStatus(taskId: string): Promise<{
147
+ getBuildStatus(taskId: string, projectId?: string): Promise<{
138
148
  session: {
139
149
  status: string | null;
140
150
  agentRunnerStatus: string | null;
141
151
  } | null;
142
152
  }>;
143
153
  getWorkspaceAttachInfo(taskId: string, sshPublicKey?: string): Promise<WorkspaceAttachInfo>;
144
- getTaskChat(taskId: string, limit?: number): Promise<unknown[]>;
145
- postToTaskChat(taskId: string, content: string): Promise<{
154
+ getTaskChat(taskId: string, limit?: number, projectId?: string): Promise<unknown[]>;
155
+ postToTaskChat(taskId: string, content: string, projectId?: string): Promise<{
146
156
  messageId: string;
147
157
  }>;
148
- getTaskCli(taskId: string, limit?: number, source?: string): Promise<{
158
+ getTaskCli(taskId: string, limit?: number, source?: string, projectId?: string): Promise<{
149
159
  type: string;
150
160
  data: Record<string, unknown>;
151
161
  timestamp: string;
152
162
  }[]>;
153
- listTags(): Promise<{
163
+ listTags(projectId?: string): Promise<{
154
164
  id: string;
155
165
  name: string;
156
166
  color: string;
157
167
  }[]>;
158
- getProjectSummary(): Promise<unknown>;
159
- approveTask(taskId: string): Promise<{
168
+ getProjectSummary(projectId?: string): Promise<unknown>;
169
+ approveTask(taskId: string, projectId?: string): Promise<{
160
170
  status: string;
161
171
  }>;
162
- requestChanges(taskId: string, feedback: string): Promise<void>;
163
- approveAndMergePR(childTaskId: string): Promise<{
172
+ requestChanges(taskId: string, feedback: string, projectId?: string): Promise<void>;
173
+ approveAndMergePR(childTaskId: string, projectId?: string): Promise<{
164
174
  merged: boolean;
165
175
  childTaskId: string;
166
176
  prNumber: number;
167
177
  }>;
168
- listTaskFiles(taskId: string): Promise<unknown[]>;
178
+ listTaskFiles(taskId: string, projectId?: string): Promise<unknown[]>;
169
179
  getAttachment(taskId: string, fileId: string, opts?: {
170
180
  offset?: number;
171
181
  maxBytes?: number;
182
+ projectId?: string;
172
183
  }): Promise<unknown>;
173
184
  requestFileUpload(taskId: string, params: {
174
185
  fileName: string;
175
186
  mimeType: string;
176
187
  fileSize: number;
188
+ projectId?: string;
177
189
  }): Promise<{
178
190
  fileId: string;
179
191
  uploadUrl: string;
180
192
  }>;
181
- confirmFileUpload(taskId: string, fileId: string, comment?: string): Promise<{
193
+ confirmFileUpload(taskId: string, fileId: string, comment?: string, projectId?: string): Promise<{
182
194
  fileId: string;
183
195
  fileName: string;
184
196
  downloadUrl?: string;
185
197
  messageId?: string;
186
198
  }>;
187
- createRelease(taskIds?: string[]): Promise<{
199
+ createRelease(taskIds?: string[], projectId?: string): Promise<{
188
200
  taskId: string;
189
201
  version: string;
190
202
  }>;
191
203
  createPullRequest(params: {
204
+ projectId?: string;
192
205
  taskId: string;
193
206
  title: string;
194
207
  body: string;
@@ -199,6 +212,7 @@ declare class ConveyorConnection {
199
212
  prUrl: string;
200
213
  }>;
201
214
  createSubtask(params: {
215
+ projectId?: string;
202
216
  parentTaskId: string;
203
217
  title: string;
204
218
  description?: string;
@@ -210,6 +224,7 @@ declare class ConveyorConnection {
210
224
  slug: string;
211
225
  }>;
212
226
  updateSubtask(params: {
227
+ projectId?: string;
213
228
  subtaskId: string;
214
229
  title?: string;
215
230
  description?: string;
@@ -221,38 +236,52 @@ declare class ConveyorConnection {
221
236
  id: string;
222
237
  status: string;
223
238
  }>;
224
- listSubtasks(taskId: string): Promise<unknown[]>;
225
- deleteSubtask(subtaskId: string): Promise<{
239
+ listSubtasks(taskId: string, projectId?: string): Promise<unknown[]>;
240
+ deleteSubtask(subtaskId: string, projectId?: string): Promise<{
226
241
  deleted: boolean;
227
242
  }>;
228
- getDependencies(taskId: string): Promise<unknown[]>;
243
+ getDependencies(taskId: string, projectId?: string): Promise<unknown[]>;
229
244
  addDependency(params: {
245
+ projectId?: string;
230
246
  taskId: string;
231
247
  dependsOnSlugOrId: string;
232
248
  }): Promise<{
233
249
  success: boolean;
234
250
  }>;
235
251
  removeDependency(params: {
252
+ projectId?: string;
236
253
  taskId: string;
237
254
  dependsOnSlugOrId: string;
238
255
  }): Promise<{
239
256
  success: boolean;
240
257
  }>;
241
- listManualTests(taskId: string): Promise<Array<{
258
+ listManualTests(taskId: string, projectId?: string): Promise<Array<{
242
259
  id: string;
243
260
  type: string;
244
261
  title: string;
245
262
  ordinal: number;
246
263
  createdAt: string;
247
264
  checked: boolean;
265
+ failures: Array<{
266
+ userName: string | null;
267
+ reason: string | null;
268
+ createdAt: string;
269
+ }>;
248
270
  }>>;
249
271
  setManualTests(taskId: string, items: Array<{
250
272
  title: string;
251
- }>): Promise<{
273
+ }>, projectId?: string): Promise<{
252
274
  created: number;
253
275
  skipped: number;
254
276
  }>;
277
+ editManualTest(taskId: string, title: string, newTitle: string, projectId?: string): Promise<{
278
+ updated: boolean;
279
+ }>;
280
+ removeManualTest(taskId: string, title: string, projectId?: string): Promise<{
281
+ removed: boolean;
282
+ }>;
255
283
  createSuggestion(params: {
284
+ projectId?: string;
256
285
  title: string;
257
286
  description?: string;
258
287
  tagNames?: string[];
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@rallycry/conveyor-mcp",
3
- "version": "3.4.0",
3
+ "version": "3.6.0",
4
4
  "description": "Conveyor MCP server for Claude Code PM integration",
5
5
  "keywords": [
6
6
  "claude",
@@ -1 +0,0 @@
1
- {"version":3,"sources":["../src/connection.ts"],"sourcesContent":["/* oxlint-disable max-lines -- central MCP socket client; split after service-call helpers are extracted */\nimport { io, type Socket } from \"socket.io-client\";\n\nexport interface ConveyorMcpConfig {\n apiUrl: string;\n projectToken: string;\n projectId: string;\n}\n\n/** A single PTY output frame broadcast on the `pty:data` room event. */\nexport interface PtyDataChunk {\n sessionId: string;\n seq: number;\n data: string;\n cols?: number;\n rows?: number;\n}\n\n/** Ring-buffer snapshot returned by `ptyAttach` for catch-up replay. */\nexport interface PtyAttachSnapshot {\n sessionId: string;\n chunks: { seq: number; data: string }[];\n cols: number;\n rows: number;\n totalBytes: number;\n}\n\n/**\n * Result of `getActivePtySession`. `sessionId` is null until the cloud agent\n * has booted a PTY and produced at least one ring frame (readiness signal).\n */\nexport interface ActivePtySession {\n sessionId: string | null;\n cols?: number;\n rows?: number;\n}\n\nexport interface WorkspaceAttachInfo {\n taskId: string;\n sessionId: string;\n workspaceRoot: string;\n tunnelUrl: string;\n attachToken: string;\n expiresAt: string;\n previewPorts: Array<{ port: number; label: string }>;\n previewUrls: Record<string, string>;\n ssh?: {\n remotePort: number;\n preferredLocalPort: number;\n username: string;\n };\n}\n\ntype SocketCallback = (response: { success: boolean; data?: unknown; error?: string }) => void;\n\n/** Auth failures never recover on retry — match them to fail fast with guidance. */\nconst AUTH_ERROR_RE = /token|unauthor|forbidden|not authenticated|invalid auth/i;\n\n/**\n * Turn a raw server error into an actionable message. Most errors pass through\n * unchanged; the common-but-opaque \"Insufficient permissions\" gets the missing\n * context about why (project role too low) and how to fix it.\n */\nfunction enrichToolError(error?: string, fallback?: string): string {\n const base = error ?? fallback ?? \"Request failed\";\n if (/insufficient permissions/i.test(base)) {\n return (\n `${base}: your Conveyor project role can't perform this action. ` +\n `Creating/updating tasks, builds, and PRs requires Moderate access or higher — ` +\n `ask a project admin to raise your role.`\n );\n }\n return base;\n}\n\nexport class ConveyorConnection {\n private socket: Socket | null = null;\n private config: ConveyorMcpConfig;\n\n constructor(config: ConveyorMcpConfig) {\n this.config = config;\n }\n\n get projectId(): string {\n return this.config.projectId;\n }\n\n connect(): Promise<void> {\n return new Promise((resolve, reject) => {\n let settled = false;\n let attempts = 0;\n const maxAttempts = 15;\n\n this.socket = io(this.config.apiUrl, {\n auth: {\n projectToken: this.config.projectToken,\n runnerMode: \"project\",\n },\n transports: [\"websocket\"],\n reconnection: true,\n reconnectionAttempts: Infinity,\n reconnectionDelay: 2000,\n reconnectionDelayMax: 30000,\n randomizationFactor: 0.3,\n extraHeaders: { \"ngrok-skip-browser-warning\": \"true\" },\n });\n\n this.socket.on(\"connect\", () => {\n if (!settled) {\n settled = true;\n // Subscribe to project room for events\n this.socket?.emit(\"projectService:subscribe\", { id: this.config.projectId });\n resolve();\n }\n });\n\n this.socket.on(\"connect_error\", (err) => {\n const message = err?.message ?? \"unknown error\";\n // An auth rejection (e.g. \"Invalid project token\") will never succeed on\n // retry, so fail fast with an actionable message instead of silently\n // retrying for ~30s and then dying with a generic error.\n if (!settled && AUTH_ERROR_RE.test(message)) {\n settled = true;\n this.socket?.close();\n reject(\n new Error(\n `Conveyor rejected the connection: ${message}. The project token is ` +\n `likely invalid or expired — regenerate it in the web app ` +\n `(User Settings → Connect Claude Code) and update CONVEYOR_PROJECT_TOKEN.`,\n ),\n );\n return;\n }\n attempts++;\n if (!settled && attempts >= maxAttempts) {\n settled = true;\n reject(new Error(`Failed to connect to ${this.config.apiUrl}: ${message}`));\n }\n });\n });\n }\n\n // ── Service method call (agentSessionService:*) ─────────────────────\n\n private call<T>(method: string, data: unknown, timeoutMs = 15_000): Promise<T> {\n const socket = this.socket;\n if (!socket) throw new Error(\"Not connected\");\n\n const TIMEOUT_MS = timeoutMs;\n\n return Promise.race([\n new Promise<T>((resolve, reject) => {\n socket.emit(`agentSessionService:${method}`, data, ((response: {\n success: boolean;\n data?: T;\n error?: string;\n }) => {\n if (response.success) {\n resolve(response.data as T);\n } else {\n reject(new Error(enrichToolError(response.error, `${method} failed`)));\n }\n }) as SocketCallback);\n }),\n new Promise<never>((_, reject) => {\n setTimeout(() => {\n reject(new Error(`Request timed out: ${method}`));\n }, TIMEOUT_MS);\n }),\n ]);\n }\n\n /**\n * Fire-and-forget emit (no ack). The quickdraw-core server method handler\n * invokes the ack callback via optional chaining, so omitting it still runs\n * the full auth/schema/ACL pipeline — it just skips the response round-trip.\n * Used for high-frequency PTY input/resize so each keystroke does not arm a\n * 15s timeout timer.\n */\n private emit(method: string, data: unknown): void {\n const socket = this.socket;\n if (!socket) throw new Error(\"Not connected\");\n socket.emit(`agentSessionService:${method}`, data);\n }\n\n private callService<T>(\n serviceName: string,\n method: string,\n data: unknown,\n timeoutMs = 15_000,\n ): Promise<T> {\n const socket = this.socket;\n if (!socket) throw new Error(\"Not connected\");\n return Promise.race([\n new Promise<T>((resolve, reject) => {\n socket.emit(`${serviceName}:${method}`, data, ((response: {\n success: boolean;\n data?: T;\n error?: string;\n }) => {\n if (response.success) {\n resolve(response.data as T);\n } else {\n reject(new Error(enrichToolError(response.error, `${method} failed`)));\n }\n }) as SocketCallback);\n }),\n new Promise<never>((_, reject) => {\n setTimeout(() => {\n reject(new Error(`Request timed out: ${serviceName}:${method}`));\n }, timeoutMs);\n }),\n ]);\n }\n\n // ── Task Queries ────────────────────────────────────────────────────\n\n listTasks(params: {\n status?: string;\n assigneeId?: string;\n unassigned?: boolean;\n limit?: number;\n }): Promise<unknown[]> {\n return this.call(\"listProjectTasks\", {\n projectId: this.config.projectId,\n ...params,\n });\n }\n\n getTask(taskId: string): Promise<unknown> {\n return this.call(\"getProjectTask\", {\n projectId: this.config.projectId,\n taskId,\n });\n }\n\n searchTasks(params: {\n tagNames?: string[];\n searchQuery?: string;\n statusFilters?: string[];\n assigneeId?: string;\n unassigned?: boolean;\n limit?: number;\n }): Promise<unknown[]> {\n return this.call(\"searchProjectTasks\", {\n projectId: this.config.projectId,\n ...params,\n });\n }\n\n // ── Task Mutations ──────────────────────────────────────────────────\n\n createTask(params: {\n title: string;\n description?: string;\n plan?: string;\n status?: string;\n }): Promise<{ id: string; slug: string }> {\n return this.call(\"createProjectTask\", {\n projectId: this.config.projectId,\n ...params,\n });\n }\n\n updateTask(params: {\n taskId: string;\n title?: string;\n description?: string;\n plan?: string;\n status?: string;\n assignedUserId?: string | null;\n }): Promise<{ id: string; status: string }> {\n return this.call(\"updateProjectTask\", {\n projectId: this.config.projectId,\n ...params,\n });\n }\n\n // ── Reviewers & Members ─────────────────────────────────────────────\n\n addReviewer(params: {\n taskId: string;\n userId: string;\n }): Promise<{ taskId: string; reviewers: Array<{ userId: string; name: string | null }> }> {\n return this.call(\"addProjectTaskReviewer\", {\n projectId: this.config.projectId,\n ...params,\n });\n }\n\n removeReviewer(params: {\n taskId: string;\n userId: string;\n }): Promise<{ taskId: string; reviewers: Array<{ userId: string; name: string | null }> }> {\n return this.call(\"removeProjectTaskReviewer\", {\n projectId: this.config.projectId,\n ...params,\n });\n }\n\n listProjectMembers(): Promise<\n Array<{ userId: string; name: string | null; email: string; level: string }>\n > {\n return this.call(\"listProjectMembers\", {\n projectId: this.config.projectId,\n });\n }\n\n // ── Build Management ────────────────────────────────────────────────\n\n startBuild(taskId: string): Promise<{ taskId: string; status: string }> {\n return this.call(\"startProjectBuild\", {\n projectId: this.config.projectId,\n taskId,\n });\n }\n\n stopBuild(taskId: string): Promise<{ taskId: string; stopped: boolean }> {\n return this.call(\"stopProjectBuild\", {\n projectId: this.config.projectId,\n taskId,\n });\n }\n\n getBuildStatus(taskId: string): Promise<{\n session: { status: string | null; agentRunnerStatus: string | null } | null;\n }> {\n // Uses getProjectTask and extracts the session field\n return this.call<Record<string, unknown>>(\"getProjectTask\", {\n projectId: this.config.projectId,\n taskId,\n }).then((task) => ({\n session:\n (task?.session as {\n status: string | null;\n agentRunnerStatus: string | null;\n }) ?? null,\n }));\n }\n\n getWorkspaceAttachInfo(taskId: string, sshPublicKey?: string): Promise<WorkspaceAttachInfo> {\n return this.callService(\"cloudBuildService\", \"getWorkspaceAttachInfo\", {\n taskId,\n sshPublicKey,\n });\n }\n\n // ── Chat ────────────────────────────────────────────────────────────\n\n getTaskChat(taskId: string, limit?: number): Promise<unknown[]> {\n // Uses getProjectTask and extracts chatMessages\n return this.call<Record<string, unknown>>(\"getProjectTask\", {\n projectId: this.config.projectId,\n taskId,\n }).then((task) => {\n const messages = (task?.chatMessages ?? []) as unknown[];\n return limit ? messages.slice(-limit) : messages;\n });\n }\n\n postToTaskChat(taskId: string, content: string): Promise<{ messageId: string }> {\n return this.call(\"postToProjectTaskChat\", {\n projectId: this.config.projectId,\n taskId,\n content,\n });\n }\n\n // ── CLI History ─────────────────────────────────────────────────────\n\n getTaskCli(\n taskId: string,\n limit?: number,\n source?: string,\n ): Promise<{ type: string; data: Record<string, unknown>; timestamp: string }[]> {\n return this.call(\"getProjectTaskCli\", {\n projectId: this.config.projectId,\n taskId,\n limit,\n source,\n });\n }\n\n // ── Project Info ────────────────────────────────────────────────────\n\n listTags(): Promise<{ id: string; name: string; color: string }[]> {\n return this.call(\"listProjectTags\", {\n projectId: this.config.projectId,\n });\n }\n\n getProjectSummary(): Promise<unknown> {\n return this.call(\"getProjectSummary\", {\n projectId: this.config.projectId,\n });\n }\n\n // ── Review Flow ─────────────────────────────────────────────────────\n\n async approveTask(taskId: string): Promise<{ status: string }> {\n // Determine next status based on current status\n const task = (await this.call(\"getProjectTask\", {\n projectId: this.config.projectId,\n taskId,\n })) as { status: string } | null;\n\n if (!task) throw new Error(\"Task not found\");\n\n const nextStatus = task.status === \"ReviewPR\" ? \"ReviewDev\" : \"Complete\";\n\n const result = await this.updateTask({ taskId, status: nextStatus });\n return { status: result.status };\n }\n\n async requestChanges(taskId: string, feedback: string): Promise<void> {\n // Post feedback to task chat then move to InProgress\n await this.postToTaskChat(taskId, feedback);\n await this.updateTask({ taskId, status: \"InProgress\" });\n }\n\n approveAndMergePR(\n childTaskId: string,\n ): Promise<{ merged: boolean; childTaskId: string; prNumber: number }> {\n return this.call(\"approveProjectMergePR\", {\n projectId: this.config.projectId,\n childTaskId,\n });\n }\n\n // ── File Attachments ────────────────────────────────────────────────\n\n listTaskFiles(taskId: string): Promise<unknown[]> {\n return this.call(\"listProjectTaskFiles\", {\n projectId: this.config.projectId,\n taskId,\n });\n }\n\n getAttachment(\n taskId: string,\n fileId: string,\n opts?: { offset?: number; maxBytes?: number },\n ): Promise<unknown> {\n const payload: Record<string, unknown> = {\n projectId: this.config.projectId,\n taskId,\n fileId,\n };\n if (opts?.offset !== undefined) payload.offset = opts.offset;\n if (opts?.maxBytes !== undefined) payload.maxBytes = opts.maxBytes;\n return this.call(\"getProjectAttachment\", payload);\n }\n\n requestFileUpload(\n taskId: string,\n params: { fileName: string; mimeType: string; fileSize: number },\n ): Promise<{ fileId: string; uploadUrl: string }> {\n return this.call(\"requestProjectFileUpload\", {\n projectId: this.config.projectId,\n taskId,\n ...params,\n });\n }\n\n confirmFileUpload(\n taskId: string,\n fileId: string,\n comment?: string,\n ): Promise<{ fileId: string; fileName: string; downloadUrl?: string; messageId?: string }> {\n const payload: Record<string, unknown> = {\n projectId: this.config.projectId,\n taskId,\n fileId,\n };\n if (comment !== undefined) payload.comment = comment;\n // Confirm verifies the object in GCS before marking it uploaded — allow\n // extra headroom for the storage round-trips.\n return this.call(\"confirmProjectFileUpload\", payload, 30_000);\n }\n\n // ── Releases ────────────────────────────────────────────────────────\n\n createRelease(taskIds?: string[]): Promise<{ taskId: string; version: string }> {\n // Full releases create the release branch + PR against GitHub synchronously;\n // allow the same headroom as createPullRequest.\n return this.call(\"createProjectRelease\", { projectId: this.config.projectId, taskIds }, 45_000);\n }\n\n // ── Pull Requests ───────────────────────────────────────────────────\n\n createPullRequest(params: {\n taskId: string;\n title: string;\n body: string;\n head?: string;\n base?: string;\n }): Promise<{ prNumber: number; prUrl: string }> {\n // PR creation makes several sequential GitHub API calls (token, create,\n // existing-PR lookup); allow more headroom than the default request timeout.\n return this.call(\n \"createProjectPullRequest\",\n { projectId: this.config.projectId, ...params },\n 45_000,\n );\n }\n\n // ── Subtasks ────────────────────────────────────────────────────────\n\n createSubtask(params: {\n parentTaskId: string;\n title: string;\n description?: string;\n plan?: string;\n ordinal?: number;\n storyPointValue?: number;\n }): Promise<{ id: string; slug: string }> {\n return this.call(\"createProjectSubtask\", {\n projectId: this.config.projectId,\n ...params,\n });\n }\n\n updateSubtask(params: {\n subtaskId: string;\n title?: string;\n description?: string;\n plan?: string;\n status?: string;\n ordinal?: number;\n storyPointValue?: number;\n }): Promise<{ id: string; status: string }> {\n return this.call(\"updateProjectSubtask\", {\n projectId: this.config.projectId,\n ...params,\n });\n }\n\n listSubtasks(taskId: string): Promise<unknown[]> {\n return this.call(\"listProjectSubtasks\", {\n projectId: this.config.projectId,\n taskId,\n });\n }\n\n deleteSubtask(subtaskId: string): Promise<{ deleted: boolean }> {\n return this.call(\"deleteProjectSubtask\", {\n projectId: this.config.projectId,\n subtaskId,\n });\n }\n\n // ── Dependencies ────────────────────────────────────────────────────\n\n getDependencies(taskId: string): Promise<unknown[]> {\n return this.call(\"getProjectTaskDependencies\", {\n projectId: this.config.projectId,\n taskId,\n });\n }\n\n addDependency(params: {\n taskId: string;\n dependsOnSlugOrId: string;\n }): Promise<{ success: boolean }> {\n return this.call(\"addProjectTaskDependency\", {\n projectId: this.config.projectId,\n ...params,\n });\n }\n\n removeDependency(params: {\n taskId: string;\n dependsOnSlugOrId: string;\n }): Promise<{ success: boolean }> {\n return this.call(\"removeProjectTaskDependency\", {\n projectId: this.config.projectId,\n ...params,\n });\n }\n\n // ── Manual Test Checklist ───────────────────────────────────────────\n\n listManualTests(taskId: string): Promise<\n Array<{\n id: string;\n type: string;\n title: string;\n ordinal: number;\n createdAt: string;\n checked: boolean;\n }>\n > {\n return this.call(\"listProjectManualTests\", {\n projectId: this.config.projectId,\n taskId,\n });\n }\n\n setManualTests(\n taskId: string,\n items: Array<{ title: string }>,\n ): Promise<{ created: number; skipped: number }> {\n return this.call(\"setProjectManualTests\", {\n projectId: this.config.projectId,\n taskId,\n items,\n });\n }\n\n // ── Suggestions ─────────────────────────────────────────────────────\n\n createSuggestion(params: {\n title: string;\n description?: string;\n tagNames?: string[];\n }): Promise<{ id: string; merged: boolean; mergedIntoId?: string }> {\n return this.call(\"createProjectSuggestion\", {\n projectId: this.config.projectId,\n ...params,\n });\n }\n\n // ── PTY Tunnel Relay (reuses the S2 pty:* envelope) ─────────────────\n\n /**\n * Poll target: returns the active cloud PTY session for a task once its ring\n * buffer has frames. `sessionId` is resolved server-side from `taskId` (never\n * accepted from the wire), preserving the one-active-session-per-task invariant.\n */\n getActivePtySession(taskId: string): Promise<ActivePtySession> {\n return this.call(\"getActivePtySession\", { taskId });\n }\n\n /** Fetch the ring-buffer snapshot for catch-up replay on (re)attach. */\n ptyAttach(sessionId: string): Promise<PtyAttachSnapshot> {\n return this.call(\"ptyAttach\", { sessionId });\n }\n\n /**\n * Join the session room so `pty:data` frames are delivered. Uses the standard\n * quickdraw-core subscribe envelope; \"Read\" is sufficient for output streaming.\n */\n subscribeToSession(sessionId: string): void {\n const socket = this.socket;\n if (!socket) throw new Error(\"Not connected\");\n socket.emit(\"agentSessionService:subscribe\", {\n entryId: sessionId,\n requiredLevel: \"Read\",\n });\n }\n\n /** Relay a stdin chunk to the cloud PTY (raw utf8, fire-and-forget). */\n ptyInput(sessionId: string, data: string): void {\n this.emit(\"ptyInput\", { sessionId, data });\n }\n\n /** Relay a terminal resize to the cloud PTY (fire-and-forget). */\n ptyResize(sessionId: string, cols: number, rows: number): void {\n this.emit(\"ptyResize\", { sessionId, cols, rows });\n }\n\n /** Subscribe to raw PTY output frames. Returns an unsubscribe function. */\n onPtyData(handler: (chunk: PtyDataChunk) => void): () => void {\n const socket = this.socket;\n if (!socket) throw new Error(\"Not connected\");\n socket.on(\"pty:data\", handler as (...args: unknown[]) => void);\n return () => {\n socket.off(\"pty:data\", handler as (...args: unknown[]) => void);\n };\n }\n\n // ── Connection lifecycle ────────────────────────────────────────────\n\n disconnect(): void {\n this.socket?.disconnect();\n this.socket = null;\n }\n}\n"],"mappings":"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","names":[]}