@rallycry/conveyor-mcp 4.3.26 → 4.3.27

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.
@@ -407,6 +407,25 @@ var ConveyorConnection = class {
407
407
  tag: params.tag
408
408
  });
409
409
  }
410
+ /** One page of a tag's attachment gallery, newest label first. */
411
+ listTagAttachments(params) {
412
+ const payload = {
413
+ projectId: this.resolveProjectId(params.projectId),
414
+ tag: params.tag
415
+ };
416
+ if (params.limit !== void 0) payload.limit = params.limit;
417
+ if (params.offset !== void 0) payload.offset = params.offset;
418
+ return this.call("listProjectTagAttachments", payload);
419
+ }
420
+ /** Replace the glossary tags on a file that is already uploaded. */
421
+ setFileTags(params) {
422
+ return this.call("setProjectFileTags", {
423
+ projectId: this.resolveProjectId(params.projectId),
424
+ taskId: params.taskId,
425
+ fileId: params.fileId,
426
+ tags: params.tags
427
+ });
428
+ }
410
429
  getProjectSummary(projectId) {
411
430
  return this.call("getProjectSummary", {
412
431
  projectId: this.resolveProjectId(projectId)
@@ -772,4 +791,4 @@ var ConveyorConnection = class {
772
791
  export {
773
792
  ConveyorConnection
774
793
  };
775
- //# sourceMappingURL=chunk-U36E7QJA.js.map
794
+ //# sourceMappingURL=chunk-Y6ZJUNDX.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/connection.ts","../../shared/dist/socket-core/index.js"],"sourcesContent":["/* oxlint-disable max-lines -- central MCP socket client; split after service-call helpers are extracted */\nimport { io, type Socket } from \"socket.io-client\";\nimport { buildConveyorSocketOptions, callWithAck } from \"@project/shared/socket-core\";\n\nexport interface ConveyorMcpConfig {\n apiUrl: string;\n projectToken: string;\n projectId?: string;\n /**\n * Optional default board (sub-project) scope. When set, unqualified\n * task create/list/search default to this board instead of the whole\n * project — so a connection scoped to a board lands cards on that board.\n */\n subProjectId?: string;\n}\n\n/** Actions a connection is authorized for, mirrors ConveyorCapability in @project/shared. */\nexport type ConveyorCapability = \"read\" | \"create\" | \"update\" | \"chat\" | \"files\" | \"build\";\n\n/** Immutable id + human name/slug + web URL for one connection scope level. */\nexport interface ConnectionScopeRef {\n id: string;\n name: string;\n slug: string;\n url: string;\n}\n\n/** Result of `get_connection_context` — effective identity + scope + grants. */\nexport interface ConnectionContext {\n account: { userId: string; name: string | null; email: string };\n project: ConnectionScopeRef & {\n githubRepoOwner: string | null;\n githubRepoName: string | null;\n };\n subProject: (ConnectionScopeRef & { rootPath: string | null }) | null;\n role: string;\n capabilities: ConveyorCapability[];\n managementUrls: { board: string; projectSettings: string; memberSettings: string };\n summary: string;\n}\n\n/** One layer of the verify_connection ladder. */\nexport interface VerifyConnectionLayer {\n key: string;\n label: string;\n ok: boolean;\n detail: string;\n}\n\n/** Result of `verify_connection` — a layered, verify-by-scope probe. */\nexport interface VerifyConnectionResult {\n ok: boolean;\n layers: VerifyConnectionLayer[];\n summary: string;\n nextAction: string | null;\n scope: {\n projectId: string;\n projectName: string;\n subProjectId: string | null;\n subProjectName: string | null;\n capabilities: ConveyorCapability[];\n };\n}\n\n/** The project + board a task mutation actually landed on. Returned so a caller\n * can confirm where the write went (name-vs-board-label ambiguity guard). */\nexport interface EffectiveScope {\n projectId: string;\n subProjectId: string | null;\n}\n\n/** One board under the connected project, for list_accessible_subprojects. */\nexport interface AccessibleSubproject {\n id: string;\n name: string;\n slug: string;\n url: string;\n rootPath: string | null;\n role: string;\n capabilities: ConveyorCapability[];\n}\n\n/**\n * Canonical risk levels — kept in lockstep with `RISK_LEVELS` in\n * `@project/shared` (conveyor-mcp is published standalone and does not depend on\n * the shared package, so the vocabulary is duplicated here).\n */\nexport type RiskLevel = \"critical\" | \"high\" | \"medium\" | \"low\";\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 /** \"codespace\" means preview-only — GitHub owns the VM's forwarded ports, so\n * `tunnelUrl`/`attachToken`/`ssh` are null/absent. Older servers omit it. */\n backend?: \"gke\" | \"codespace\";\n tunnelUrl: string | null;\n attachToken: string | null;\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\n/** One Cloud Logging entry as returned by the API's queryProjectGcpLogs. */\nexport interface GcpLogEntry {\n timestamp: string;\n severity: string;\n message: string;\n resource: Record<string, string>;\n labels: Record<string, string>;\n resourceType?: string;\n insertId?: string;\n trace?: string;\n httpRequest?: { status?: number; method?: string; url?: string; latencyMs?: number };\n payload?: string;\n payloadTruncated?: boolean;\n}\n\nexport interface GcpLogQueryResult {\n entries: GcpLogEntry[];\n hasMore: boolean;\n nextPageToken?: string;\n scopedServices?: string[];\n error?: string;\n}\n\n/** Loki entries normalize onto the same row shape as Cloud Logging entries. */\nexport interface GrafanaLogQueryResult {\n entries: GcpLogEntry[];\n hasMore: boolean;\n /** The LogQL the server composed or executed — agents can iterate on it. */\n logql?: string;\n error?: string;\n}\n\n/**\n * One row of the project onboarding checklist. Mirrors the API's\n * `ReadinessCheck` (conveyor-mcp is published standalone and does not depend on\n * `@project/shared`, so the shape is duplicated here).\n */\nexport interface OnboardingCheck {\n key: string;\n status: \"ok\" | \"warn\" | \"fail\";\n reason: string;\n fix?: {\n label: string;\n href?: string | null;\n action?: string;\n };\n}\n\n/** Result of `get_onboarding_status` — the project's setup readiness report. */\nexport interface OnboardingStatus {\n ok: boolean;\n checks: OnboardingCheck[];\n bypassedAt: string | null;\n}\n\n/** The single next onboarding step. Mirrors the API's `OnboardingStep`. */\nexport interface OnboardingStep {\n key: string;\n status: \"warn\" | \"fail\";\n title: string;\n reason: string;\n guidance: string;\n autoFixable: boolean;\n fix?: {\n label: string;\n href?: string | null;\n action?: string;\n };\n connectUrls?: Record<string, string>;\n}\n\n/** Result of `get_onboarding_step` — the choose-your-own-adventure step driver.\n * Mirrors the API's `OnboardingStepReport`. */\nexport interface OnboardingStepReport {\n done: boolean;\n ok: boolean;\n bypassedAt: string | null;\n step: OnboardingStep | null;\n remaining: Array<{ key: string; status: \"warn\" | \"fail\"; reason: string }>;\n checks: OnboardingCheck[];\n}\n\nexport interface MoveCardResult {\n cardId: string;\n sourceProjectId: string;\n destinationProjectId: string;\n slug: string;\n clearedFields: string[];\n}\n\nexport interface ProjectConnectUrls {\n gcpConnect: string;\n gcpSettings: string;\n memberSettings: string;\n projectSettings: string;\n setupWizard: string;\n}\n\nexport interface TagSummary {\n id: string;\n name: string;\n color: string;\n description?: string | null;\n}\n\n/** A tag context-path link (rule/doc/file/folder) loaded into agent context.\n * A locator turns the link into a VERIFIED repo link — mirrors\n * TagContextLink in @project/shared types/tag-types.ts. */\nexport interface ContextPathInput {\n type: \"rule\" | \"doc\" | \"file\" | \"folder\";\n path: string;\n label?: string;\n locator?: string;\n locatorType?: \"test\" | \"code\";\n}\n\nexport interface PrioritySummary {\n id: string;\n value: number;\n name: string;\n color: string;\n description?: string | null;\n}\n\nconst NO_PROJECT_SELECTED_MESSAGE =\n \"No Conveyor project selected. Pass projectId to this tool or configure CONVEYOR_PROJECT_ID.\";\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.resolveProjectId();\n }\n\n private resolveProjectId(projectId?: string): string {\n const resolved = projectId ?? this.config.projectId;\n if (!resolved) throw new Error(NO_PROJECT_SELECTED_MESSAGE);\n return resolved;\n }\n\n /** The configured default board (CONVEYOR_SUBPROJECT_ID), if any. */\n get defaultSubProjectId(): string | undefined {\n return this.config.subProjectId;\n }\n\n /**\n * Resolve the board scope for a task create/list/search:\n * - `null` explicitly means \"the whole project\" (never fall back to the default).\n * - `undefined` (not passed) falls back to the connection's default board.\n * - a concrete id is used as-is.\n * Returning `undefined` leaves the request unscoped (whole project).\n */\n private resolveSubProjectId(subProjectId?: string | null): string | undefined {\n if (subProjectId === null) return undefined;\n return subProjectId ?? this.config.subProjectId;\n }\n\n private normalizeProjectList(response: unknown): unknown[] {\n if (Array.isArray(response)) return response;\n if (typeof response !== \"object\" || response === null) return [];\n const record = response as Record<string, unknown>;\n if (Array.isArray(record.items)) return record.items;\n if (Array.isArray(record.projects)) return record.projects;\n if (Array.isArray(record.data)) return record.data;\n return [];\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(\n this.config.apiUrl,\n buildConveyorSocketOptions({\n projectToken: this.config.projectToken,\n runnerMode: \"project\",\n }),\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 your coding agent) and update CONVEYOR_USER_TOKEN ` +\n `(or legacy 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 return callWithAck<T>(socket, `agentSessionService:${method}`, data, {\n timeoutMs,\n makeTimeoutError: () => new Error(`Request timed out: ${method}`),\n makeFailureError: (error) => new Error(enrichToolError(error, `${method} failed`)),\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 callWithAck<T>(socket, `${serviceName}:${method}`, data, {\n timeoutMs,\n makeTimeoutError: () => new Error(`Request timed out: ${serviceName}:${method}`),\n makeFailureError: (error) => new Error(enrichToolError(error, `${method} failed`)),\n });\n }\n\n // ── GCP Logs ────────────────────────────────────────────────────────\n\n queryGcpLogs(params: {\n projectId?: string;\n env?: \"prod\" | \"dev\" | \"claudespace\";\n severity?: string;\n services?: string[];\n sqlInstances?: string[];\n allServices?: boolean;\n search?: string;\n filter?: string;\n startTime?: string;\n endTime?: string;\n limit?: number;\n pageToken?: string;\n }): Promise<GcpLogQueryResult> {\n const { projectId, ...rest } = params;\n return this.call(\n \"queryProjectGcpLogs\",\n { projectId: this.resolveProjectId(projectId), ...rest },\n 30_000,\n );\n }\n\n // ── Grafana Logs ────────────────────────────────────────────────────\n\n queryGrafanaLogs(params: {\n projectId?: string;\n env?: \"prod\" | \"dev\";\n services?: string[];\n level?: \"debug\" | \"info\" | \"warn\" | \"error\" | \"fatal\";\n search?: string;\n logql?: string;\n startTime?: string;\n endTime?: string;\n limit?: number;\n }): Promise<GrafanaLogQueryResult> {\n const { projectId, ...rest } = params;\n return this.call(\n \"queryProjectGrafanaLogs\",\n { projectId: this.resolveProjectId(projectId), ...rest },\n 30_000,\n );\n }\n\n // ── Task Queries ────────────────────────────────────────────────────\n\n listTasks(params: {\n projectId?: string;\n status?: string;\n typeFilters?: string[];\n assigneeId?: string;\n unassigned?: boolean;\n subProjectId?: string | null;\n limit?: number;\n }): Promise<unknown[]> {\n const { projectId, subProjectId, ...rest } = params;\n return this.call(\"listProjectTasks\", {\n projectId: this.resolveProjectId(projectId),\n subProjectId: this.resolveSubProjectId(subProjectId),\n ...rest,\n });\n }\n\n getTask(taskId: string, projectId?: string): Promise<unknown> {\n return this.call(\"getProjectTask\", {\n projectId: this.resolveProjectId(projectId),\n taskId,\n });\n }\n\n getCardBySlug(slug: string, projectId?: string): Promise<unknown> {\n return this.call(\"getProjectTask\", {\n projectId: this.resolveProjectId(projectId),\n taskId: slug,\n });\n }\n\n searchTasks(params: {\n projectId?: string;\n tagNames?: string[];\n tagMatch?: \"any\" | \"all\";\n includeChildTags?: boolean;\n searchQuery?: string;\n statusFilters?: string[];\n typeFilters?: string[];\n assigneeId?: string;\n unassigned?: boolean;\n subProjectId?: string | null;\n limit?: number;\n }): Promise<unknown[]> {\n const { projectId, subProjectId, ...rest } = params;\n return this.call(\"searchProjectTasks\", {\n projectId: this.resolveProjectId(projectId),\n subProjectId: this.resolveSubProjectId(subProjectId),\n ...rest,\n });\n }\n\n // ── Task Mutations ──────────────────────────────────────────────────\n\n async createTask(params: {\n projectId?: string;\n title: string;\n description?: string;\n plan?: string;\n status?: string;\n subProjectId?: string | null;\n tags?: string[];\n }): Promise<{ id: string; slug: string; effectiveScope: EffectiveScope }> {\n const { projectId, subProjectId, tags, ...rest } = params;\n const resolvedProjectId = this.resolveProjectId(projectId);\n const resolvedSubProjectId = this.resolveSubProjectId(subProjectId);\n const result = await this.call<{ id: string; slug: string }>(\"createProjectTask\", {\n projectId: resolvedProjectId,\n subProjectId: resolvedSubProjectId,\n ...rest,\n });\n if (tags && tags.length > 0) {\n await this.assignTagsToTask(result.id, resolvedProjectId, tags);\n }\n return {\n ...result,\n effectiveScope: {\n projectId: resolvedProjectId,\n subProjectId: resolvedSubProjectId ?? null,\n },\n };\n }\n\n async updateTask(params: {\n projectId?: string;\n taskId: string;\n title?: string;\n description?: string;\n plan?: string;\n status?: string;\n risk?: RiskLevel | null;\n storyPointValue?: number | null;\n assignedUserId?: string | null;\n subProjectId?: string | null;\n addTags?: string[];\n removeTags?: string[];\n }): Promise<{\n id: string;\n status: string | null;\n risk: RiskLevel | null;\n storyPointValue: number | null;\n assignedUserId: string | null;\n addedTags: string[];\n removedTags: string[];\n }> {\n const { projectId, addTags, removeTags, ...rest } = params;\n const resolvedProjectId = this.resolveProjectId(projectId);\n\n // A core-field update is only sent when at least one non-tag field changed —\n // UpdateProjectTaskRequestSchema rejects a no-op (tag-only) update.\n const hasCoreUpdate =\n rest.title !== undefined ||\n rest.description !== undefined ||\n rest.plan !== undefined ||\n rest.status !== undefined ||\n rest.risk !== undefined ||\n rest.storyPointValue !== undefined ||\n rest.assignedUserId !== undefined ||\n rest.subProjectId !== undefined;\n\n // Tag mutations route through TagService (same path as the web UI) so the\n // task:tagsAssigned/Removed events, Slack sync, and card re-emit all fire.\n const removedTags = removeTags ?? [];\n const addedTags = addTags ?? [];\n if (removedTags.length > 0) {\n await this.removeTagsFromTask(rest.taskId, resolvedProjectId, removedTags);\n }\n if (addedTags.length > 0) {\n await this.assignTagsToTask(rest.taskId, resolvedProjectId, addedTags);\n }\n\n if (hasCoreUpdate) {\n const result = await this.call<{\n id: string;\n status: string;\n risk: RiskLevel | null;\n storyPointValue: number | null;\n assignedUserId: string | null;\n }>(\"updateProjectTask\", { projectId: resolvedProjectId, ...rest });\n return { ...result, addedTags, removedTags };\n }\n\n return {\n id: rest.taskId,\n status: null,\n risk: null,\n storyPointValue: null,\n assignedUserId: null,\n addedTags,\n removedTags,\n };\n }\n\n /** Resolve tag names to IDs within a project, throwing on any unknown name. */\n private async resolveTagIds(projectId: string, names: string[]): Promise<string[]> {\n if (names.length === 0) return [];\n const tags = await this.listTags(projectId);\n const byName = new Map(tags.map((t) => [t.name.toLowerCase(), t.id]));\n const ids: string[] = [];\n const unknown: string[] = [];\n for (const name of names) {\n const id = byName.get(name.toLowerCase());\n if (id) ids.push(id);\n else unknown.push(name);\n }\n if (unknown.length > 0) {\n const available = tags.map((t) => t.name).join(\", \") || \"(none)\";\n throw new Error(\n `Unknown tag name(s): ${unknown.join(\", \")}. Available tags: ${available}. ` +\n `Create the tag first with manage_tags.`,\n );\n }\n return [...new Set(ids)];\n }\n\n private async assignTagsToTask(\n taskId: string,\n projectId: string,\n names: string[],\n ): Promise<void> {\n const tagIds = await this.resolveTagIds(projectId, names);\n if (tagIds.length === 0) return;\n await this.callService(\"tagService\", \"assignToTask\", { taskId, tagIds });\n }\n\n private async removeTagsFromTask(\n taskId: string,\n projectId: string,\n names: string[],\n ): Promise<void> {\n const tagIds = await this.resolveTagIds(projectId, names);\n if (tagIds.length === 0) return;\n await this.callService(\"tagService\", \"removeFromTask\", { taskId, tagIds });\n }\n\n /** Guarded status transition used by the review tools (approve/request). */\n transitionTaskStatus(params: {\n projectId?: string;\n taskId: string;\n toStatus: string;\n expectedFromStatus?: string;\n risk?: RiskLevel;\n }): Promise<{ id: string; status: string; risk: RiskLevel | null }> {\n const { projectId, ...rest } = params;\n return this.call(\"transitionProjectTaskStatus\", {\n projectId: this.resolveProjectId(projectId),\n ...rest,\n });\n }\n\n moveCard(params: {\n projectId?: string;\n taskId: string;\n destinationProjectId: string;\n }): Promise<MoveCardResult> {\n const { projectId, ...rest } = params;\n return this.call(\"moveProjectCard\", {\n projectId: this.resolveProjectId(projectId),\n ...rest,\n });\n }\n\n // ── Reviewers & Members ─────────────────────────────────────────────\n\n addReviewer(params: {\n projectId?: string;\n taskId: string;\n userId: string;\n }): Promise<{ taskId: string; reviewers: Array<{ userId: string; name: string | null }> }> {\n const { projectId, ...rest } = params;\n return this.call(\"addProjectTaskReviewer\", {\n projectId: this.resolveProjectId(projectId),\n ...rest,\n });\n }\n\n removeReviewer(params: {\n projectId?: string;\n taskId: string;\n userId: string;\n }): Promise<{ taskId: string; reviewers: Array<{ userId: string; name: string | null }> }> {\n const { projectId, ...rest } = params;\n return this.call(\"removeProjectTaskReviewer\", {\n projectId: this.resolveProjectId(projectId),\n ...rest,\n });\n }\n\n listProjectMembers(\n projectId?: string,\n ): Promise<Array<{ userId: string; name: string | null; email: string; level: string }>> {\n return this.call(\"listProjectMembers\", {\n projectId: this.resolveProjectId(projectId),\n });\n }\n\n async listProjects(): Promise<unknown[]> {\n const response = await this.call(\"listAccessibleProjects\", { pageSize: 100 });\n return this.normalizeProjectList(response);\n }\n\n // ── Build Management ────────────────────────────────────────────────\n\n startBuild(taskId: string, projectId?: string): Promise<{ taskId: string; status: string }> {\n return this.call(\"startProjectBuild\", {\n projectId: this.resolveProjectId(projectId),\n taskId,\n });\n }\n\n stopBuild(taskId: string, projectId?: string): Promise<{ taskId: string; stopped: boolean }> {\n return this.call(\"stopProjectBuild\", {\n projectId: this.resolveProjectId(projectId),\n taskId,\n });\n }\n\n sleepTask(\n taskId: string,\n projectId?: string,\n ): Promise<{ taskId: string; codespaceStatus: string }> {\n return this.callService(\"cloudBuildService\", \"sleepCloudBuild\", {\n taskId,\n projectId: this.resolveProjectId(projectId),\n });\n }\n\n resumeTask(taskId: string, projectId?: string): Promise<{ status: string }> {\n return this.callService(\"cloudBuildService\", \"wakeCodespace\", {\n taskId,\n projectId: this.resolveProjectId(projectId),\n });\n }\n\n deleteTaskEnvironment(\n taskId: string,\n projectId?: string,\n ): Promise<{ taskId: string; status: string }> {\n return this.callService(\"cloudBuildService\", \"deleteTaskEnvironment\", {\n taskId,\n projectId: this.resolveProjectId(projectId),\n });\n }\n\n getBuildStatus(\n taskId: string,\n projectId?: string,\n ): 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.resolveProjectId(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, projectId?: string): Promise<unknown[]> {\n // Uses getProjectTask and extracts chatMessages\n return this.call<Record<string, unknown>>(\"getProjectTask\", {\n projectId: this.resolveProjectId(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(\n taskId: string,\n content: string,\n projectId?: string,\n ): Promise<{ messageId: string }> {\n return this.call(\"postToProjectTaskChat\", {\n projectId: this.resolveProjectId(projectId),\n taskId,\n content,\n });\n }\n\n // ── CLI History ─────────────────────────────────────────────────────\n\n getTaskCli(\n taskId: string,\n limit?: number,\n source?: string,\n projectId?: string,\n ): Promise<{ type: string; data: Record<string, unknown>; timestamp: string }[]> {\n return this.call(\"getProjectTaskCli\", {\n projectId: this.resolveProjectId(projectId),\n taskId,\n limit,\n source,\n });\n }\n\n // ── Session / Pod State ─────────────────────────────────────────────\n\n getTaskSessions(\n taskId: string,\n projectId?: string,\n ): Promise<\n Array<{\n taskId: string;\n slug: string;\n title: string;\n type: string;\n status: string;\n codeReviewStatus: string | null;\n codeReviewAttempts: number;\n workspaces: Array<{\n id: string;\n purpose: string;\n desiredState: string;\n observedState: string;\n branch: string | null;\n checkoutRef: string | null;\n createdAt: string;\n updatedAt: string;\n pod: { name: string; namespace: string; phase: string; imageUri: string | null } | null;\n sessions: Array<{\n id: string;\n role: string;\n mode: string;\n status: string;\n userId: string;\n leaseUntil: string | null;\n createdAt: string;\n }>;\n }>;\n sessions: Array<{\n id: string;\n provider: string;\n instanceName: string | null;\n status: string;\n agentRunnerStatus: string | null;\n lastHeartbeatAt: string | null;\n agentRunningAt: string | null;\n lastAgentEvent: string | null;\n deletionRequestedAt: string | null;\n deletionAttempts: number;\n createdAt: string;\n stoppedAt: string | null;\n }>;\n }>\n > {\n return this.call(\"getProjectTaskSessions\", {\n projectId: this.resolveProjectId(projectId),\n taskId,\n });\n }\n\n // ── Project Info ────────────────────────────────────────────────────\n\n listTags(projectId?: string): Promise<\n {\n id: string;\n name: string;\n color: string;\n description: string | null;\n parentTagIds: string[];\n childTagIds: string[];\n hasOverview: boolean;\n /** Files labelled as examples of the tag — read the tiles with list_tag_attachments. */\n attachmentCount: number;\n /** Rule/doc/file/folder links the tag wires into agent context; `[]` when none. */\n contextPaths: ContextPathInput[];\n }[]\n > {\n return this.call(\"listProjectTags\", {\n projectId: this.resolveProjectId(projectId),\n });\n }\n\n getTag(params: { projectId?: string; tag: string }): Promise<unknown> {\n return this.call(\"getProjectTag\", {\n projectId: this.resolveProjectId(params.projectId),\n tag: params.tag,\n });\n }\n\n /** One page of a tag's attachment gallery, newest label first. */\n listTagAttachments(params: {\n projectId?: string;\n tag: string;\n limit?: number;\n offset?: number;\n }): Promise<unknown> {\n const payload: Record<string, unknown> = {\n projectId: this.resolveProjectId(params.projectId),\n tag: params.tag,\n };\n if (params.limit !== undefined) payload.limit = params.limit;\n if (params.offset !== undefined) payload.offset = params.offset;\n return this.call(\"listProjectTagAttachments\", payload);\n }\n\n /** Replace the glossary tags on a file that is already uploaded. */\n setFileTags(params: {\n projectId?: string;\n taskId: string;\n fileId: string;\n tags: string[];\n }): Promise<{\n fileId: string;\n fileName: string;\n appliedTags?: string[];\n unknownTags?: string[];\n }> {\n return this.call(\"setProjectFileTags\", {\n projectId: this.resolveProjectId(params.projectId),\n taskId: params.taskId,\n fileId: params.fileId,\n tags: params.tags,\n });\n }\n\n getProjectSummary(projectId?: string): Promise<unknown> {\n return this.call(\"getProjectSummary\", {\n projectId: this.resolveProjectId(projectId),\n });\n }\n\n // ── Connection Scope & Verification ─────────────────────────────────\n\n /** Effective account/project/board identity + capabilities for this token. */\n getConnectionContext(projectId?: string): Promise<ConnectionContext> {\n return this.call(\"getConnectionContext\", {\n projectId: this.resolveProjectId(projectId),\n subProjectId: this.config.subProjectId ?? null,\n });\n }\n\n /** Layered verify-by-scope probe (auth → account → project → board →\n * capabilities → read). Proves create/update on the intended board. */\n verifyConnection(params?: {\n projectId?: string;\n intendedActions?: ConveyorCapability[];\n }): Promise<VerifyConnectionResult> {\n return this.call(\"verifyConnection\", {\n projectId: this.resolveProjectId(params?.projectId),\n subProjectId: this.config.subProjectId ?? null,\n intendedActions: params?.intendedActions,\n });\n }\n\n /** Boards under the connected project with id/name/slug/url/role/capabilities. */\n listAccessibleSubprojects(projectId?: string): Promise<AccessibleSubproject[]> {\n return this.call(\"listAccessibleSubprojects\", {\n projectId: this.resolveProjectId(projectId),\n });\n }\n\n getOnboardingStatus(projectId?: string): Promise<OnboardingStatus> {\n // Each readiness probe fans out to GitHub / GCP, so allow more headroom\n // than the default request timeout.\n return this.call(\n \"getProjectOnboardingStatus\",\n { projectId: this.resolveProjectId(projectId) },\n 30_000,\n );\n }\n\n getOnboardingStep(projectId?: string): Promise<OnboardingStepReport> {\n // Reuses the same readiness probes (GitHub / GCP fan-out), so allow the same\n // extended timeout as get_onboarding_status.\n return this.call(\n \"getProjectOnboardingStep\",\n { projectId: this.resolveProjectId(projectId) },\n 30_000,\n );\n }\n\n // ── Review Flow ─────────────────────────────────────────────────────\n\n async approveTask(\n taskId: string,\n projectId?: string,\n risk?: RiskLevel,\n ): Promise<{ status: string }> {\n const targetProjectId = this.resolveProjectId(projectId);\n // Determine next status based on current status\n const task = (await this.call(\"getProjectTask\", {\n projectId: targetProjectId,\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 // Guarded transition; approval attempts a raise-only \"low\" risk by default.\n const result = await this.transitionTaskStatus({\n projectId: targetProjectId,\n taskId,\n toStatus: nextStatus,\n expectedFromStatus: task.status,\n risk: risk ?? \"low\",\n });\n return { status: result.status };\n }\n\n async requestChanges(\n taskId: string,\n feedback: string,\n projectId?: string,\n risk?: RiskLevel,\n ): Promise<void> {\n const targetProjectId = this.resolveProjectId(projectId);\n const task = (await this.call(\"getProjectTask\", {\n projectId: targetProjectId,\n taskId,\n })) as { status: string } | null;\n if (!task) throw new Error(\"Task not found\");\n\n // Post feedback to task chat then move to InProgress via the guarded\n // transition; requested changes attempt a raise-only \"medium\" risk.\n await this.postToTaskChat(taskId, feedback, targetProjectId);\n await this.transitionTaskStatus({\n projectId: targetProjectId,\n taskId,\n toStatus: \"InProgress\",\n expectedFromStatus: task.status,\n risk: risk ?? \"medium\",\n });\n }\n\n approveAndMergePR(\n childTaskId: string,\n projectId?: string,\n ): Promise<{ merged: boolean; childTaskId: string; prNumber: number }> {\n return this.call(\"approveProjectMergePR\", {\n projectId: this.resolveProjectId(projectId),\n childTaskId,\n });\n }\n\n // ── File Attachments ────────────────────────────────────────────────\n\n listTaskFiles(taskId: string, projectId?: string): Promise<unknown[]> {\n return this.call(\"listProjectTaskFiles\", {\n projectId: this.resolveProjectId(projectId),\n taskId,\n });\n }\n\n getAttachment(\n taskId: string,\n fileId: string,\n opts?: { offset?: number; maxBytes?: number; projectId?: string },\n ): Promise<unknown> {\n const payload: Record<string, unknown> = {\n projectId: this.resolveProjectId(opts?.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; projectId?: string },\n ): Promise<{ fileId: string; uploadUrl: string }> {\n const { projectId, ...rest } = params;\n return this.call(\"requestProjectFileUpload\", {\n projectId: this.resolveProjectId(projectId),\n taskId,\n ...rest,\n });\n }\n\n confirmFileUpload(\n taskId: string,\n fileId: string,\n comment?: string,\n projectId?: string,\n tags?: string[],\n ): Promise<{\n fileId: string;\n fileName: string;\n downloadUrl?: string;\n messageId?: string;\n appliedTags?: string[];\n unknownTags?: string[];\n }> {\n const payload: Record<string, unknown> = {\n projectId: this.resolveProjectId(projectId),\n taskId,\n fileId,\n };\n if (comment !== undefined) payload.comment = comment;\n if (tags?.length) payload.tags = tags;\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(\n taskIds?: string[],\n projectId?: string,\n ): 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(\n \"createProjectRelease\",\n { projectId: this.resolveProjectId(projectId), taskIds },\n 45_000,\n );\n }\n\n addTasksToRelease(\n taskIds: string[],\n projectId?: string,\n ): Promise<{ releaseTaskId: string; added: number; releaseBranchUpdated: boolean }> {\n // Adding cards also merges the latest dev into the release branch on\n // GitHub — allow the same headroom as createRelease.\n return this.call(\n \"addTasksToProjectRelease\",\n { projectId: this.resolveProjectId(projectId), taskIds },\n 45_000,\n );\n }\n\n // ── Pull Requests ───────────────────────────────────────────────────\n\n createPullRequest(params: {\n projectId?: string;\n taskId: string;\n title: string;\n body: string;\n head?: string;\n base?: string;\n }): Promise<{ prNumber: number; prUrl: string }> {\n const { projectId, ...rest } = params;\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.resolveProjectId(projectId), ...rest },\n 45_000,\n );\n }\n\n // ── Subtasks ────────────────────────────────────────────────────────\n\n async createSubtask(params: {\n projectId?: string;\n parentTaskId: string;\n title: string;\n description?: string;\n plan?: string;\n ordinal?: number;\n storyPointValue?: number;\n followParentStatus?: boolean;\n dependsOn?: string[];\n tags?: string[];\n }): Promise<{ id: string; slug: string }> {\n const { projectId, tags, ...rest } = params;\n const resolvedProjectId = this.resolveProjectId(projectId);\n const result = await this.call<{ id: string; slug: string }>(\"createProjectSubtask\", {\n projectId: resolvedProjectId,\n ...rest,\n });\n if (tags && tags.length > 0) {\n await this.assignTagsToTask(result.id, resolvedProjectId, tags);\n }\n return result;\n }\n\n updateSubtask(params: {\n projectId?: string;\n subtaskId: string;\n title?: string;\n description?: string;\n plan?: string;\n status?: string;\n ordinal?: number;\n storyPointValue?: number;\n followParentStatus?: boolean;\n dependsOn?: string[];\n }): Promise<{ id: string; status: string }> {\n const { projectId, ...rest } = params;\n return this.call(\"updateProjectSubtask\", {\n projectId: this.resolveProjectId(projectId),\n ...rest,\n });\n }\n\n listSubtasks(taskId: string, projectId?: string): Promise<unknown[]> {\n return this.call(\"listProjectSubtasks\", {\n projectId: this.resolveProjectId(projectId),\n taskId,\n });\n }\n\n deleteSubtask(subtaskId: string, projectId?: string): Promise<{ deleted: boolean }> {\n return this.call(\"deleteProjectSubtask\", {\n projectId: this.resolveProjectId(projectId),\n subtaskId,\n });\n }\n\n // ── Dependencies ────────────────────────────────────────────────────\n\n getDependencies(taskId: string, projectId?: string): Promise<unknown[]> {\n return this.call(\"getProjectTaskDependencies\", {\n projectId: this.resolveProjectId(projectId),\n taskId,\n });\n }\n\n addDependency(params: {\n projectId?: string;\n taskId: string;\n dependsOnSlugOrId: string;\n }): Promise<{ success: boolean }> {\n const { projectId, ...rest } = params;\n return this.call(\"addProjectTaskDependency\", {\n projectId: this.resolveProjectId(projectId),\n ...rest,\n });\n }\n\n removeDependency(params: {\n projectId?: string;\n taskId: string;\n dependsOnSlugOrId: string;\n }): Promise<{ success: boolean }> {\n const { projectId, ...rest } = params;\n return this.call(\"removeProjectTaskDependency\", {\n projectId: this.resolveProjectId(projectId),\n ...rest,\n });\n }\n\n // ── Manual Test Checklist ───────────────────────────────────────────\n\n listManualTests(\n taskId: string,\n projectId?: string,\n ): Promise<\n Array<{\n id: string;\n type: string;\n title: string;\n ordinal: number;\n createdAt: string;\n checked: boolean;\n failures: Array<{ userName: string | null; reason: string | null; createdAt: string }>;\n }>\n > {\n return this.call(\"listProjectManualTests\", {\n projectId: this.resolveProjectId(projectId),\n taskId,\n });\n }\n\n setManualTests(\n taskId: string,\n items: Array<{ title: string }>,\n projectId?: string,\n ): Promise<{ created: number; skipped: number }> {\n return this.call(\"setProjectManualTests\", {\n projectId: this.resolveProjectId(projectId),\n taskId,\n items,\n });\n }\n\n editManualTest(\n taskId: string,\n title: string,\n newTitle: string,\n projectId?: string,\n ): Promise<{ updated: boolean }> {\n return this.call(\"editProjectManualTest\", {\n projectId: this.resolveProjectId(projectId),\n taskId,\n title,\n newTitle,\n });\n }\n\n removeManualTest(\n taskId: string,\n title: string,\n projectId?: string,\n ): Promise<{ removed: boolean }> {\n return this.call(\"removeProjectManualTest\", {\n projectId: this.resolveProjectId(projectId),\n taskId,\n title,\n });\n }\n\n approveManualTest(\n taskId: string,\n title: string,\n projectId?: string,\n ): Promise<{ approved: boolean }> {\n return this.call(\"approveProjectManualTest\", {\n projectId: this.resolveProjectId(projectId),\n taskId,\n title,\n });\n }\n\n rejectManualTest(\n taskId: string,\n title: string,\n reason: string,\n projectId?: string,\n ): Promise<{ rejected: boolean }> {\n return this.call(\"rejectProjectManualTest\", {\n projectId: this.resolveProjectId(projectId),\n taskId,\n title,\n reason,\n });\n }\n\n queryManualTests(params: {\n projectId?: string;\n cardStatuses?: string[];\n testStatuses?: Array<\"open\" | \"approved\" | \"rejected\">;\n }): Promise<\n Array<{\n taskId: string;\n slug: string;\n title: string;\n status: string;\n tests: Array<{\n id: string;\n title: string;\n status: \"open\" | \"approved\" | \"rejected\";\n failures: Array<{ userName: string | null; reason: string | null; createdAt: string }>;\n }>;\n }>\n > {\n const { projectId, ...rest } = params;\n return this.call(\"queryProjectManualTests\", {\n projectId: this.resolveProjectId(projectId),\n ...rest,\n });\n }\n\n // ── Project Configuration ──────────────────────────────────────────\n\n getConnectUrls(projectId?: string): Promise<ProjectConnectUrls> {\n return this.call(\"getProjectConnectUrls\", { projectId: this.resolveProjectId(projectId) });\n }\n\n updateProjectConfig(params: {\n projectId?: string;\n name?: string;\n description?: string;\n settings?: Record<string, unknown>;\n }): Promise<unknown> {\n const { projectId, ...rest } = params;\n return this.callService(\"projectService\", \"updateProject\", {\n id: this.resolveProjectId(projectId),\n ...rest,\n });\n }\n\n updateProjectAgentDefaults(params: {\n projectId?: string;\n defaultPmAgentId?: string | null;\n defaultTaskAgentId?: string | null;\n defaultReviewerAgentId?: string | null;\n helperAgentId?: string | null;\n }): Promise<unknown> {\n const { projectId, ...rest } = params;\n return this.callService(\"projectService\", \"updateProjectDefaults\", {\n id: this.resolveProjectId(projectId),\n ...rest,\n });\n }\n\n listTagsDetailed(projectId?: string): Promise<TagSummary[]> {\n return this.callService(\"tagService\", \"listTagsWithCounts\", {\n projectId: this.resolveProjectId(projectId),\n });\n }\n\n createTag(params: {\n projectId?: string;\n name: string;\n color?: string;\n description?: string;\n overview?: string;\n /** Repo file to source the overview from (stored overview stays as the pending fallback). */\n overviewPath?: string;\n /** Parents to link at create time (multi-parent DAG). */\n parentTagIds?: string[];\n contextPaths?: ContextPathInput[];\n }): Promise<{ id: string }> {\n const { projectId, ...rest } = params;\n return this.callService(\"tagService\", \"createTag\", {\n projectId: this.resolveProjectId(projectId),\n ...rest,\n });\n }\n\n updateTag(params: {\n id: string;\n name?: string;\n color?: string;\n description?: string;\n overview?: string | null;\n /** Repo file to source the overview from; null clears back to the stored overview. */\n overviewPath?: string | null;\n parentTagIds?: string[];\n reason?: string;\n contextPaths?: ContextPathInput[];\n }): Promise<unknown> {\n return this.callService(\"tagService\", \"updateTag\", params);\n }\n\n deleteTag(id: string): Promise<unknown> {\n return this.callService(\"tagService\", \"deleteTag\", { id });\n }\n\n previewTagMerge(params: { sourceTagId: string; targetTagId: string }): Promise<unknown> {\n return this.callService(\"tagService\", \"previewTagMerge\", params);\n }\n\n mergeTag(params: {\n sourceTagId: string;\n targetTagId: string;\n reason?: string;\n }): Promise<unknown> {\n return this.callService(\"tagService\", \"mergeTag\", params);\n }\n\n listPriorities(projectId?: string): Promise<PrioritySummary[]> {\n return this.callService(\"priorityService\", \"listByProject\", {\n projectId: this.resolveProjectId(projectId),\n });\n }\n\n createPriority(params: {\n projectId?: string;\n value: number;\n name: string;\n color: string;\n description?: string;\n }): Promise<{ id: string }> {\n const { projectId, ...rest } = params;\n return this.callService(\"priorityService\", \"createPriority\", {\n projectId: this.resolveProjectId(projectId),\n ...rest,\n });\n }\n\n updatePriority(params: {\n id: string;\n value?: number;\n name?: string;\n color?: string;\n description?: string;\n }): Promise<unknown> {\n return this.callService(\"priorityService\", \"updatePriority\", params);\n }\n\n deletePriority(id: string): Promise<unknown> {\n return this.callService(\"priorityService\", \"deletePriority\", { id });\n }\n\n // ── Suggestions ─────────────────────────────────────────────────────\n\n createSuggestion(params: {\n projectId?: string;\n title: string;\n description?: string;\n tagNames?: string[];\n }): Promise<{ id: string; merged: boolean; mergedIntoId?: string }> {\n const { projectId, ...rest } = params;\n return this.call(\"createProjectSuggestion\", {\n projectId: this.resolveProjectId(projectId),\n ...rest,\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","// src/socket-core/socket-options.ts\nfunction buildConveyorSocketOptions(auth) {\n return {\n auth,\n transports: [\"websocket\"],\n reconnection: true,\n reconnectionAttempts: Infinity,\n reconnectionDelay: 2e3,\n reconnectionDelayMax: 3e4,\n randomizationFactor: 0.3,\n extraHeaders: { \"ngrok-skip-browser-warning\": \"true\" }\n };\n}\n\n// src/socket-core/call-with-ack.ts\nfunction callWithAck(socket, event, payload, options) {\n const { timeoutMs, requireData = false, makeTimeoutError, makeFailureError } = options;\n return new Promise((resolve, reject) => {\n let settled = false;\n const timer = setTimeout(() => {\n if (settled) return;\n settled = true;\n reject(makeTimeoutError());\n }, timeoutMs);\n socket.emit(event, payload, (response) => {\n if (settled) return;\n settled = true;\n clearTimeout(timer);\n if (response.success && (!requireData || response.data !== void 0)) {\n resolve(response.data);\n } else {\n reject(makeFailureError(response.error));\n }\n });\n });\n}\nfunction waitForConnected(socket, timeoutMs, makeTimeoutError) {\n if (socket.connected) return Promise.resolve();\n return new Promise((resolve, reject) => {\n const cleanup = () => {\n clearTimeout(timer);\n socket.off(\"connect\", onConnect);\n };\n const onConnect = () => {\n cleanup();\n resolve();\n };\n const timer = setTimeout(() => {\n cleanup();\n reject(makeTimeoutError());\n }, timeoutMs);\n socket.once(\"connect\", onConnect);\n });\n}\nexport {\n buildConveyorSocketOptions,\n callWithAck,\n waitForConnected\n};\n"],"mappings":"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","names":[]}
package/dist/cli.js CHANGED
@@ -1,7 +1,7 @@
1
1
  #!/usr/bin/env node
2
2
  import {
3
3
  ConveyorConnection
4
- } from "./chunk-U36E7QJA.js";
4
+ } from "./chunk-Y6ZJUNDX.js";
5
5
 
6
6
  // src/cli.ts
7
7
  import { createRequire } from "module";
@@ -143,14 +143,14 @@ var f = {
143
143
  return { kind: "nullable", inner };
144
144
  }
145
145
  };
146
- function compileString(z9, spec) {
147
- let schema = z9.string();
146
+ function compileString(z10, spec) {
147
+ let schema = z10.string();
148
148
  if (spec.min !== void 0) schema = schema.min(spec.min);
149
149
  if (spec.max !== void 0) schema = schema.max(spec.max);
150
150
  return schema;
151
151
  }
152
- function compileNumber(z9, spec) {
153
- let schema = z9.number();
152
+ function compileNumber(z10, spec) {
153
+ let schema = z10.number();
154
154
  if (spec.int) schema = schema.int();
155
155
  if (spec.positive) schema = schema.positive();
156
156
  if (spec.nonnegative) schema = schema.nonnegative();
@@ -158,41 +158,41 @@ function compileNumber(z9, spec) {
158
158
  if (spec.max !== void 0) schema = schema.max(spec.max);
159
159
  return schema;
160
160
  }
161
- function compileArray(z9, spec) {
162
- let schema = z9.array(compileField(z9, spec.item));
161
+ function compileArray(z10, spec) {
162
+ let schema = z10.array(compileField(z10, spec.item));
163
163
  if (spec.min !== void 0) schema = schema.min(spec.min);
164
164
  return schema;
165
165
  }
166
- function compileBase(z9, spec) {
166
+ function compileBase(z10, spec) {
167
167
  switch (spec.kind) {
168
168
  case "string":
169
- return compileString(z9, spec);
169
+ return compileString(z10, spec);
170
170
  case "number":
171
- return compileNumber(z9, spec);
171
+ return compileNumber(z10, spec);
172
172
  case "boolean":
173
- return z9.boolean();
173
+ return z10.boolean();
174
174
  case "enum":
175
- return z9.enum([...spec.values]);
175
+ return z10.enum([...spec.values]);
176
176
  case "array":
177
- return compileArray(z9, spec);
177
+ return compileArray(z10, spec);
178
178
  case "object":
179
- return z9.object(compileShape(z9, spec.fields));
179
+ return z10.object(compileShape(z10, spec.fields));
180
180
  }
181
181
  }
182
- function compileField(z9, spec) {
182
+ function compileField(z10, spec) {
183
183
  if (spec.kind === "optional") {
184
- return compileField(z9, spec.inner).optional();
184
+ return compileField(z10, spec.inner).optional();
185
185
  }
186
186
  if (spec.kind === "nullable") {
187
- return compileField(z9, spec.inner).nullable();
187
+ return compileField(z10, spec.inner).nullable();
188
188
  }
189
- const schema = compileBase(z9, spec);
189
+ const schema = compileBase(z10, spec);
190
190
  return spec.desc === void 0 ? schema : schema.describe(spec.desc);
191
191
  }
192
- function compileShape(z9, fields) {
192
+ function compileShape(z10, fields) {
193
193
  const shape = {};
194
194
  for (const [key, spec] of Object.entries(fields)) {
195
- shape[key] = compileField(z9, spec);
195
+ shape[key] = compileField(z10, spec);
196
196
  }
197
197
  return shape;
198
198
  }
@@ -282,11 +282,11 @@ var readTaskChatContract = defineToolContract({
282
282
  var listTagsContract = defineToolContract({
283
283
  name: "list_tags",
284
284
  agent: {
285
- description: "List the project glossary: every tag's id, name, color, description, parent/child tag ids, its contextPaths (the rule/doc/file/folder links it wires into agent context \u2014 `[]` means none), and whether it carries a full overview (fetch that with get_tag). The context links ship inline, so you only need get_tag for a term's full overview. Use the ids with get_tag / update_tag.",
285
+ description: "List the project glossary: every tag's id, name, color, description, parent/child tag ids, its contextPaths (the rule/doc/file/folder links it wires into agent context \u2014 `[]` means none), whether it carries a full overview (fetch that with get_tag), and its attachmentCount (files labelled as examples of the term). The context links ship inline, so you only need get_tag for a term's full overview. Use the ids with get_tag / update_tag.",
286
286
  fields: {}
287
287
  },
288
288
  mcp: {
289
- description: "List all project tags with names, IDs, colors, descriptions, hierarchy (parent/child ids), contextPaths (the rule/doc/file/folder links each tag wires into agent context \u2014 `[]` means none), and a hasOverview flag. Context links ship inline; call get_tag only for a term's full overview. Pass projectId to target a specific project; otherwise the configured default project is used.",
289
+ description: "List all project tags with names, IDs, colors, descriptions, hierarchy (parent/child ids), contextPaths (the rule/doc/file/folder links each tag wires into agent context \u2014 `[]` means none), a hasOverview flag, and an attachmentCount (files labelled as examples of the term \u2014 read the tiles with list_tag_attachments). Context links ship inline; call get_tag only for a term's full overview. Pass projectId to target a specific project; otherwise the configured default project is used.",
290
290
  fields: {
291
291
  projectId: mcpProjectId
292
292
  }
@@ -393,13 +393,13 @@ var tagRef = f.string({
393
393
  var getTagContract = defineToolContract({
394
394
  name: "get_tag",
395
395
  agent: {
396
- description: "Read one tag's full glossary entry: description, the full markdown overview (the term's spec \u2014 philosophy, mechanics, invariants), linked files/rules (each with its verified-link status \u2014 ok/stale/unchecked \u2014 from the periodic repo check), parent/child tags, active-card count, and recent revisions with their reasons. Call this whenever a chat message, plan, or tag list points at a term you need the full context for. A response with `overviewPath` set means the overview is sourced from that repo file \u2014 prefer Reading the path from your checkout (branch-correct); the served overview is the base-branch materialization (`overviewSource.state`: ok/pending/stale).",
396
+ description: "Read one tag's full glossary entry: description, the full markdown overview (the term's spec \u2014 philosophy, mechanics, invariants), linked files/rules (each with its verified-link status \u2014 ok/stale/unchecked \u2014 from the periodic repo check), parent/child tags, active-card count, attachment count (files labelled as examples of the term), and recent revisions with their reasons. Call this whenever a chat message, plan, or tag list points at a term you need the full context for. A response with `overviewPath` set means the overview is sourced from that repo file \u2014 prefer Reading the path from your checkout (branch-correct); the served overview is the base-branch materialization (`overviewSource.state`: ok/pending/stale).",
397
397
  fields: {
398
398
  tag: tagRef
399
399
  }
400
400
  },
401
401
  mcp: {
402
- description: "Read one tag's full glossary entry \u2014 description, markdown overview, context links (each with its verified-link status: ok/stale/unchecked plus last-checked provenance), parent/child tags, active-card count, and recent revisions with provenance. A response with `overviewPath` set means the overview is sourced from that repo file at the base branch (`overviewSource.state`: ok/pending/stale) \u2014 clients with a checkout can read the path directly for the branch-correct copy. Pass projectId to target a specific project; otherwise the configured default project is used.",
402
+ description: "Read one tag's full glossary entry \u2014 description, markdown overview, context links (each with its verified-link status: ok/stale/unchecked plus last-checked provenance), parent/child tags, active-card count, attachment count (files labelled as examples of the term \u2014 read the tiles with list_tag_attachments), and recent revisions with provenance. A response with `overviewPath` set means the overview is sourced from that repo file at the base branch (`overviewSource.state`: ok/pending/stale) \u2014 clients with a checkout can read the path directly for the branch-correct copy. Pass projectId to target a specific project; otherwise the configured default project is used.",
403
403
  fields: {
404
404
  projectId: mcpProjectId,
405
405
  tag: tagRef
@@ -1182,11 +1182,25 @@ function registerManagePriorities(server2, conn2) {
1182
1182
  }
1183
1183
  );
1184
1184
  }
1185
+ function registerListTagAttachments(server2, conn2) {
1186
+ server2.tool(
1187
+ "list_tag_attachments",
1188
+ "List the files labelled as examples of one tag \u2014 the tag page's Attachments gallery, newest label first. Each tile carries the file's name, mime type, size, a downloadUrl, the card it came from, and the tag's other labels on that file. Only uploaded files appear. The page returns `hasMore` instead of a total: pass offset to read the next page. Tag names come from list_tags, which reports each tag's attachmentCount. Pass projectId to target a specific project; otherwise the configured default project is used.",
1189
+ {
1190
+ projectId: z4.string().optional().describe("Target Conveyor project ID"),
1191
+ tag: z4.string().min(1).max(100).describe("Tag id, or the exact tag name (case-insensitive)"),
1192
+ limit: z4.number().int().min(1).max(60).optional().describe("Tiles per page (default 24)"),
1193
+ offset: z4.number().int().min(0).optional().describe("Tiles to skip (paging). Default 0.")
1194
+ },
1195
+ async (params) => jsonResult(await conn2.listTagAttachments(params))
1196
+ );
1197
+ }
1185
1198
  function registerProjectConfigTools(server2, conn2) {
1186
1199
  registerGetConnectUrls(server2, conn2);
1187
1200
  registerUpdateProjectSettings(server2, conn2);
1188
1201
  registerManageTags(server2, conn2);
1189
1202
  registerGetTag(server2, conn2);
1203
+ registerListTagAttachments(server2, conn2);
1190
1204
  registerManagePriorities(server2, conn2);
1191
1205
  }
1192
1206
 
@@ -1730,7 +1744,9 @@ function registerBuildTools(server2, conn2) {
1730
1744
  // src/tools/attachments.ts
1731
1745
  import { readFile, stat } from "fs/promises";
1732
1746
  import { basename, extname } from "path";
1747
+ import { z as z7 } from "zod";
1733
1748
  var MAX_FILE_SIZE_BYTES = 25 * 1024 * 1024;
1749
+ var MAX_FILE_TAGS = 5;
1734
1750
  var MIME_BY_EXT = {
1735
1751
  ".png": "image/png",
1736
1752
  ".jpg": "image/jpeg",
@@ -1867,10 +1883,37 @@ function registerUploadAttachment(server2, conn2) {
1867
1883
  return { content: [{ type: "text", text: lines.join("\n") }] };
1868
1884
  });
1869
1885
  }
1886
+ function registerSetFileTags(server2, conn2) {
1887
+ server2.tool(
1888
+ "set_file_tags",
1889
+ "Replace the glossary tags on a file that is already uploaded \u2014 the labelling upload_attachment does at upload time, applied to an existing file. Use it to add older attachments to a tag's Attachments gallery, which is what makes a file a visible example of that tagged entity. `tags` is the FULL replacement set: the names you pass become the file's tags and any others are removed, so pass [] to clear every tag. Names are matched case-insensitively within the project; a name that matches no tag is reported back and never fails the tags that did match. Max 5. Call list_task_files for file IDs and list_tags for tag names. Pass projectId to target a specific project; otherwise the configured default project is used.",
1890
+ {
1891
+ projectId: z7.string().optional().describe("Target Conveyor project ID"),
1892
+ taskId: z7.string().describe("The task ID or slug the file is attached to"),
1893
+ fileId: z7.string().describe("The file ID to label \u2014 from list_task_files"),
1894
+ tags: z7.array(z7.string().min(1).max(100)).max(MAX_FILE_TAGS).describe(
1895
+ "Glossary tag names (or ids) the file is an example of. Replaces the file's current tags \u2014 [] clears them. Max 5."
1896
+ )
1897
+ },
1898
+ async (params) => {
1899
+ const result = await conn2.setFileTags(params);
1900
+ const lines = [
1901
+ result.appliedTags?.length ? `Tagged ${result.fileName}: ${result.appliedTags.join(", ")}.` : `Cleared all tags on ${result.fileName}.`
1902
+ ];
1903
+ if (result.unknownTags?.length) {
1904
+ lines.push(
1905
+ `No tag matched: ${result.unknownTags.join(", ")} \u2014 check the project's tag names with list_tags.`
1906
+ );
1907
+ }
1908
+ return { content: [{ type: "text", text: lines.join("\n") }] };
1909
+ }
1910
+ );
1911
+ }
1870
1912
  function registerAttachmentTools(server2, conn2) {
1871
1913
  registerListTaskFiles(server2, conn2);
1872
1914
  registerGetAttachment(server2, conn2);
1873
1915
  registerUploadAttachment(server2, conn2);
1916
+ registerSetFileTags(server2, conn2);
1874
1917
  }
1875
1918
 
1876
1919
  // src/tools/pull-request.ts
@@ -2060,7 +2103,7 @@ function registerChecklistTools(server2, conn2) {
2060
2103
  }
2061
2104
 
2062
2105
  // src/tools/workspace.ts
2063
- import { z as z7 } from "zod";
2106
+ import { z as z8 } from "zod";
2064
2107
 
2065
2108
  // src/workspace-ssh-tunnel.ts
2066
2109
  import net from "net";
@@ -2180,8 +2223,8 @@ function registerAttachInfoTool(server2, conn2) {
2180
2223
  "workspace_attach_info",
2181
2224
  "Return SSH/SFTP attach metadata for a running task Claudespace, plus hosted preview URLs and configured preview ports. Optionally installs an OpenSSH public key for this attach session.",
2182
2225
  {
2183
- taskId: z7.string().describe("The task ID"),
2184
- sshPublicKey: z7.string().optional().describe("Optional OpenSSH public key to install into the workspace")
2226
+ taskId: z8.string().describe("The task ID"),
2227
+ sshPublicKey: z8.string().optional().describe("Optional OpenSSH public key to install into the workspace")
2185
2228
  },
2186
2229
  async ({ taskId, sshPublicKey }) => {
2187
2230
  const info = await conn2.getWorkspaceAttachInfo(taskId, sshPublicKey);
@@ -2194,7 +2237,7 @@ function registerPreviewUrlsTool(server2, conn2) {
2194
2237
  "workspace_preview_urls",
2195
2238
  "Return the hosted preview URLs and preview ports for a running task Claudespace. This mirrors the web UI preview link metadata.",
2196
2239
  {
2197
- taskId: z7.string().describe("The task ID")
2240
+ taskId: z8.string().describe("The task ID")
2198
2241
  },
2199
2242
  async ({ taskId }) => {
2200
2243
  const info = await conn2.getWorkspaceAttachInfo(taskId);
@@ -2223,10 +2266,10 @@ function registerStartTunnelTool(server2, conn2, startTunnel) {
2223
2266
  "workspace_start_tunnel",
2224
2267
  "Start a local loopback tunnel through the MCP server to a running task Claudespace port. Use port 2222 for SSH/SFTP, or one of previewPorts for app access.",
2225
2268
  {
2226
- taskId: z7.string().describe("The task ID"),
2227
- port: z7.number().optional().describe("Remote Claudespace port. Defaults to SSH port 2222."),
2228
- preferredLocalPort: z7.number().optional().describe("Preferred local loopback port. If omitted, the OS chooses one."),
2229
- sshPublicKey: z7.string().optional().describe("Optional OpenSSH public key to install before opening the tunnel")
2269
+ taskId: z8.string().describe("The task ID"),
2270
+ port: z8.number().optional().describe("Remote Claudespace port. Defaults to SSH port 2222."),
2271
+ preferredLocalPort: z8.number().optional().describe("Preferred local loopback port. If omitted, the OS chooses one."),
2272
+ sshPublicKey: z8.string().optional().describe("Optional OpenSSH public key to install before opening the tunnel")
2230
2273
  },
2231
2274
  async ({ taskId, port, preferredLocalPort, sshPublicKey }) => {
2232
2275
  const info = await conn2.getWorkspaceAttachInfo(taskId, sshPublicKey);
@@ -2286,7 +2329,7 @@ function registerStopTunnelTool(server2) {
2286
2329
  "workspace_stop_tunnel",
2287
2330
  "Stop a local workspace tunnel previously opened by workspace_start_tunnel.",
2288
2331
  {
2289
- tunnelId: z7.string().describe("Tunnel id returned by workspace_start_tunnel")
2332
+ tunnelId: z8.string().describe("Tunnel id returned by workspace_start_tunnel")
2290
2333
  },
2291
2334
  async ({ tunnelId }) => {
2292
2335
  const tunnel = activeTunnels.get(tunnelId);
@@ -2305,7 +2348,7 @@ function registerWorkspaceTools(server2, conn2, deps = {}) {
2305
2348
  }
2306
2349
 
2307
2350
  // src/tools/logs.ts
2308
- import { z as z8 } from "zod";
2351
+ import { z as z9 } from "zod";
2309
2352
  var SEVERITY_ENUM = [
2310
2353
  "DEBUG",
2311
2354
  "INFO",
@@ -2441,18 +2484,18 @@ function registerGrafanaLogTool(server2, conn2) {
2441
2484
  "query_grafana_logs",
2442
2485
  "Query the project's connected Grafana (Loki) logs \u2014 the application logs shipped to Grafana Cloud/Loki, complementing query_gcp_logs (GCP infrastructure logs). Start with structured filters (env, level=error, sinceMinutes=60, services), then narrow with search; pass raw LogQL via logql only when structured filters can't express the query (it REPLACES them). The response header echoes the composed LogQL \u2014 iterate on it. Returns compact lines: '<time> <SEVERITY> [<service>] <message>'.",
2443
2486
  {
2444
- projectId: z8.string().optional().describe("Target Conveyor project ID"),
2445
- env: z8.enum(["prod", "dev"]).optional().describe("Configured Grafana env mapping to scope by (default prod)"),
2446
- sinceMinutes: z8.number().int().min(1).max(10080).optional().describe(
2487
+ projectId: z9.string().optional().describe("Target Conveyor project ID"),
2488
+ env: z9.enum(["prod", "dev"]).optional().describe("Configured Grafana env mapping to scope by (default prod)"),
2489
+ sinceMinutes: z9.number().int().min(1).max(10080).optional().describe(
2447
2490
  "Relative time window ending now, in minutes (default 60). Ignored if startTime is set."
2448
2491
  ),
2449
- startTime: z8.string().optional().describe("ISO 8601 lower bound (overrides sinceMinutes)"),
2450
- endTime: z8.string().optional().describe("ISO 8601 upper bound (default now)"),
2451
- level: z8.enum(["debug", "info", "warn", "error", "fatal"]).optional().describe("Minimum severity, inclusive \u2014 error returns error and above"),
2452
- services: z8.array(z8.string()).optional().describe("Restrict to these service_name label values"),
2453
- search: z8.string().max(256).optional().describe("Substring line filter (exact substring, not regex)"),
2454
- logql: z8.string().max(2e3).optional().describe("Advanced: raw LogQL query \u2014 REPLACES env/services/level/search composition"),
2455
- limit: z8.number().int().min(1).max(200).optional().describe("Max entries (default 50)")
2492
+ startTime: z9.string().optional().describe("ISO 8601 lower bound (overrides sinceMinutes)"),
2493
+ endTime: z9.string().optional().describe("ISO 8601 upper bound (default now)"),
2494
+ level: z9.enum(["debug", "info", "warn", "error", "fatal"]).optional().describe("Minimum severity, inclusive \u2014 error returns error and above"),
2495
+ services: z9.array(z9.string()).optional().describe("Restrict to these service_name label values"),
2496
+ search: z9.string().max(256).optional().describe("Substring line filter (exact substring, not regex)"),
2497
+ logql: z9.string().max(2e3).optional().describe("Advanced: raw LogQL query \u2014 REPLACES env/services/level/search composition"),
2498
+ limit: z9.number().int().min(1).max(200).optional().describe("Max entries (default 50)")
2456
2499
  },
2457
2500
  async (params) => {
2458
2501
  const text = await runQueryGrafanaLogs(conn2, params);
@@ -2465,25 +2508,25 @@ function registerLogTools(server2, conn2) {
2465
2508
  "query_gcp_logs",
2466
2509
  "Query Google Cloud Logging for a project's linked GCP environments \u2014 use this to investigate production or dev issues directly ('something broke on prod'). Envs: 'prod' and 'dev' are the project's Cloud Run apps + Cloud SQL databases (scoped by default to the resources linked in project settings); 'claudespace' is the project's GKE agent-pod namespace. Start broad (severity=ERROR, sinceMinutes=60), then narrow with services/search. Returns compact lines: '<time> <SEVERITY> [<source>] <message> | key=value \u2026' (the key=value tail is the entry's structured payload \u2014 error details, service/method, actor and entity ids). When the response ends with a pageToken line, pass that token back as pageToken for the next page. Pass projectId to target a specific project; otherwise the configured default project is used.",
2467
2510
  {
2468
- projectId: z8.string().optional().describe("Target Conveyor project ID"),
2469
- env: z8.enum(["prod", "dev", "claudespace"]).optional().describe("GCP environment slot to query (default prod)"),
2470
- sinceMinutes: z8.number().int().min(1).max(10080).optional().describe(
2511
+ projectId: z9.string().optional().describe("Target Conveyor project ID"),
2512
+ env: z9.enum(["prod", "dev", "claudespace"]).optional().describe("GCP environment slot to query (default prod)"),
2513
+ sinceMinutes: z9.number().int().min(1).max(10080).optional().describe(
2471
2514
  "Relative time window ending now, in minutes (default 60). Ignored if startTime is set."
2472
2515
  ),
2473
- startTime: z8.string().optional().describe("ISO 8601 lower bound (overrides sinceMinutes)"),
2474
- endTime: z8.string().optional().describe("ISO 8601 upper bound (default now)"),
2475
- severity: z8.enum(SEVERITY_ENUM).optional().describe("Minimum severity, inclusive \u2014 ERROR returns ERROR and above"),
2476
- services: z8.array(z8.string()).optional().describe(
2516
+ startTime: z9.string().optional().describe("ISO 8601 lower bound (overrides sinceMinutes)"),
2517
+ endTime: z9.string().optional().describe("ISO 8601 upper bound (default now)"),
2518
+ severity: z9.enum(SEVERITY_ENUM).optional().describe("Minimum severity, inclusive \u2014 ERROR returns ERROR and above"),
2519
+ services: z9.array(z9.string()).optional().describe(
2477
2520
  "Restrict to these Cloud Run service names (prod/dev only). Defaults to all services linked in project settings."
2478
2521
  ),
2479
- sqlInstances: z8.array(z8.string()).optional().describe("Restrict to these Cloud SQL instance names (prod/dev only)"),
2480
- allServices: z8.boolean().optional().describe(
2522
+ sqlInstances: z9.array(z9.string()).optional().describe("Restrict to these Cloud SQL instance names (prod/dev only)"),
2523
+ allServices: z9.boolean().optional().describe(
2481
2524
  "Set true to search ALL logs in the GCP project, ignoring the linked-resource scope"
2482
2525
  ),
2483
- search: z8.string().max(256).optional().describe("Free-text search across all log fields (exact substring, not regex)"),
2484
- filter: z8.string().max(1e3).optional().describe("Advanced: raw Cloud Logging filter expression, ANDed with the scope"),
2485
- limit: z8.number().int().min(1).max(200).optional().describe("Max entries per page (default 50)"),
2486
- pageToken: z8.string().optional().describe("Opaque token from a previous response to fetch the next page")
2526
+ search: z9.string().max(256).optional().describe("Free-text search across all log fields (exact substring, not regex)"),
2527
+ filter: z9.string().max(1e3).optional().describe("Advanced: raw Cloud Logging filter expression, ANDed with the scope"),
2528
+ limit: z9.number().int().min(1).max(200).optional().describe("Max entries per page (default 50)"),
2529
+ pageToken: z9.string().optional().describe("Opaque token from a previous response to fetch the next page")
2487
2530
  },
2488
2531
  async (params) => {
2489
2532
  const text = await runQueryGcpLogs(conn2, params);