@rallycry/conveyor-mcp 4.3.30 → 5.0.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/{chunk-X3EHPZNH.js → chunk-2TVN7F5E.js} +83 -0
- package/dist/cli.js +2795 -303
- package/dist/{connection-CRkBLz5w.d.ts → connection-DtGKRfga.d.ts} +78 -0
- package/dist/index.d.ts +2 -1
- package/dist/index.js +1 -1
- package/dist/tunnel-cli.js +1 -1
- package/dist/tunnel.d.ts +2 -1
- package/dist/wait-cli.js +1 -1
- package/dist/wait-runner.d.ts +2 -1
- package/dist/wait.d.ts +2 -1
- package/package.json +2 -2
package/dist/cli.js
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
2
|
import {
|
|
3
3
|
ConveyorConnection
|
|
4
|
-
} from "./chunk-
|
|
4
|
+
} from "./chunk-2TVN7F5E.js";
|
|
5
5
|
|
|
6
6
|
// src/cli.ts
|
|
7
7
|
import { createRequire } from "module";
|
|
@@ -67,54 +67,142 @@ function registerProjectTools(server2, conn2) {
|
|
|
67
67
|
}
|
|
68
68
|
|
|
69
69
|
// src/tools/connection.ts
|
|
70
|
-
import { z as
|
|
71
|
-
var CAPABILITY_ENUM = ["read", "create", "update", "chat", "files", "build"];
|
|
72
|
-
function registerConnectionTools(server2, conn2) {
|
|
73
|
-
server2.tool(
|
|
74
|
-
"get_connection_context",
|
|
75
|
-
"Resolve WHO this connection is and WHAT project/board it points at \u2014 call this FIRST, before inferring anything from names. Returns the effective account, project, and board (sub-project) each with BOTH its immutable ID and its human-readable name/slug, the granted capabilities (read/create/update/chat/files/build), management URLs, and a one-line summary. Removes the ambiguity between a project's canonical name and a board label. Pass projectId to target a specific project; otherwise the configured default project is used.",
|
|
76
|
-
{
|
|
77
|
-
projectId: z2.string().optional().describe("Target Conveyor project ID")
|
|
78
|
-
},
|
|
79
|
-
async (params) => {
|
|
80
|
-
const ctx = await conn2.getConnectionContext(params.projectId);
|
|
81
|
-
return { content: [{ type: "text", text: JSON.stringify(ctx, null, 2) }] };
|
|
82
|
-
}
|
|
83
|
-
);
|
|
84
|
-
server2.tool(
|
|
85
|
-
"verify_connection",
|
|
86
|
-
"Prove the connection is correct AND that you can actually write to the intended board \u2014 not just that auth works. Runs layered checks (auth \u2192 account \u2192 project \u2192 target board \u2192 capabilities \u2192 read) and returns a plain pass/fail with, on failure, the exact failing layer and ONE next action. Use this instead of get_project_summary to confirm setup: a summary that returns data proves auth, not scope. Pass intendedActions to verify specific capabilities (defaults to read+create+update). Pass projectId to target a specific project; otherwise the configured default project is used. The board scope comes from CONVEYOR_SUBPROJECT_ID.",
|
|
87
|
-
{
|
|
88
|
-
projectId: z2.string().optional().describe("Target Conveyor project ID"),
|
|
89
|
-
intendedActions: z2.array(z2.enum(CAPABILITY_ENUM)).optional().describe(
|
|
90
|
-
"Capabilities to verify the connection can perform (default: read, create, update)."
|
|
91
|
-
)
|
|
92
|
-
},
|
|
93
|
-
async (params) => {
|
|
94
|
-
const result = await conn2.verifyConnection(params);
|
|
95
|
-
return { content: [{ type: "text", text: JSON.stringify(result, null, 2) }] };
|
|
96
|
-
}
|
|
97
|
-
);
|
|
98
|
-
server2.tool(
|
|
99
|
-
"list_accessible_subprojects",
|
|
100
|
-
"List the boards (sub-projects) under the connected project \u2014 each with its ID, name, slug, board URL, owned root path, and the role/capabilities this token has on it. Use this to discover which board to create or list cards on (pass a returned id as subProjectId, or set CONVEYOR_SUBPROJECT_ID) instead of asking the human for one. Pass projectId to target a specific project; otherwise the configured default project is used.",
|
|
101
|
-
{
|
|
102
|
-
projectId: z2.string().optional().describe("Target Conveyor project ID")
|
|
103
|
-
},
|
|
104
|
-
async (params) => {
|
|
105
|
-
const subprojects = await conn2.listAccessibleSubprojects(params.projectId);
|
|
106
|
-
return { content: [{ type: "text", text: JSON.stringify(subprojects, null, 2) }] };
|
|
107
|
-
}
|
|
108
|
-
);
|
|
109
|
-
}
|
|
110
|
-
|
|
111
|
-
// src/tools/project-config.ts
|
|
112
|
-
import { z as z4 } from "zod";
|
|
70
|
+
import { z as z3 } from "zod";
|
|
113
71
|
|
|
114
|
-
// ../shared/dist/chunk-
|
|
72
|
+
// ../shared/dist/chunk-OKJPFFQI.js
|
|
115
73
|
var CARD_DESCRIPTION_MAX = 255;
|
|
116
74
|
var CARD_DESCRIPTION_LIMIT_MESSAGE = `Card descriptions are capped at ${CARD_DESCRIPTION_MAX} characters \u2014 write 1-2 plain sentences a non-engineer can read; put technical detail in the plan or card chat.`;
|
|
117
75
|
var CARD_DESCRIPTION_FIELD_HINT = `max ${CARD_DESCRIPTION_MAX} chars, 1-2 plain sentences a non-engineer can read \u2014 put technical detail in the plan`;
|
|
76
|
+
var SEVERITY_ENUM = [
|
|
77
|
+
"DEBUG",
|
|
78
|
+
"INFO",
|
|
79
|
+
"NOTICE",
|
|
80
|
+
"WARNING",
|
|
81
|
+
"ERROR",
|
|
82
|
+
"CRITICAL",
|
|
83
|
+
"ALERT",
|
|
84
|
+
"EMERGENCY"
|
|
85
|
+
];
|
|
86
|
+
var MAX_LINE_CHARS = 400;
|
|
87
|
+
var DEFAULT_SINCE_MINUTES = 60;
|
|
88
|
+
function truncateLine(text) {
|
|
89
|
+
const oneLine = text.replace(/\s*\n\s*/g, " \u23CE ");
|
|
90
|
+
if (oneLine.length <= MAX_LINE_CHARS) return oneLine;
|
|
91
|
+
const overflow = oneLine.length - MAX_LINE_CHARS;
|
|
92
|
+
return `${oneLine.slice(0, MAX_LINE_CHARS)}\u2026[+${overflow}c]`;
|
|
93
|
+
}
|
|
94
|
+
function entrySource(entry) {
|
|
95
|
+
return entry.resource.service_name ?? entry.resource.pod_name ?? entry.resource.database_id ?? entry.resourceType ?? "-";
|
|
96
|
+
}
|
|
97
|
+
var PAYLOAD_SKIP_KEYS = /* @__PURE__ */ new Set(["message", "severity", "timestamp", "level", "stack"]);
|
|
98
|
+
var PAYLOAD_PRIORITY = [
|
|
99
|
+
"error",
|
|
100
|
+
"outcome",
|
|
101
|
+
"serviceName",
|
|
102
|
+
"methodName",
|
|
103
|
+
"userId",
|
|
104
|
+
"taskId",
|
|
105
|
+
"sessionId",
|
|
106
|
+
"workspaceId",
|
|
107
|
+
"projectId",
|
|
108
|
+
"durationMs"
|
|
109
|
+
];
|
|
110
|
+
var PAYLOAD_VALUE_MAX_CHARS = 160;
|
|
111
|
+
function compactPayloadValue(value) {
|
|
112
|
+
const raw = typeof value === "string" ? value : JSON.stringify(value);
|
|
113
|
+
const flat = (raw ?? "undefined").replace(/\s+/g, " ");
|
|
114
|
+
const quoted = typeof value === "string" && /[\s"]/.test(flat) ? JSON.stringify(flat) : flat;
|
|
115
|
+
return quoted.length > PAYLOAD_VALUE_MAX_CHARS ? `${quoted.slice(0, PAYLOAD_VALUE_MAX_CHARS)}\u2026` : quoted;
|
|
116
|
+
}
|
|
117
|
+
function formatPayloadSuffix(payloadJson) {
|
|
118
|
+
if (!payloadJson) return "";
|
|
119
|
+
let parsed;
|
|
120
|
+
try {
|
|
121
|
+
parsed = JSON.parse(payloadJson);
|
|
122
|
+
} catch {
|
|
123
|
+
return "";
|
|
124
|
+
}
|
|
125
|
+
if (!parsed || typeof parsed !== "object" || Array.isArray(parsed)) return "";
|
|
126
|
+
const obj = parsed;
|
|
127
|
+
const rank = (key) => {
|
|
128
|
+
const i = PAYLOAD_PRIORITY.indexOf(key);
|
|
129
|
+
return i === -1 ? PAYLOAD_PRIORITY.length : i;
|
|
130
|
+
};
|
|
131
|
+
const parts = Object.keys(obj).filter((k) => !PAYLOAD_SKIP_KEYS.has(k) && obj[k] !== void 0 && obj[k] !== null).sort((a, b) => rank(a) - rank(b) || a.localeCompare(b)).map((k) => `${k}=${compactPayloadValue(obj[k])}`);
|
|
132
|
+
return parts.length > 0 ? ` | ${parts.join(" ")}` : "";
|
|
133
|
+
}
|
|
134
|
+
function formatLogEntryLine(entry) {
|
|
135
|
+
const httpPrefix = entry.httpRequest?.status ? `http ${entry.httpRequest.status} ${entry.httpRequest.method ?? ""} ${entry.httpRequest.url ?? ""}`.trim() + " \u2014 " : "";
|
|
136
|
+
return `${entry.timestamp} ${entry.severity.padEnd(7)} [${entrySource(entry)}] ${truncateLine(
|
|
137
|
+
`${httpPrefix}${entry.message}${formatPayloadSuffix(entry.payload)}`
|
|
138
|
+
)}`;
|
|
139
|
+
}
|
|
140
|
+
async function runQueryGcpLogs(port, params, now = Date.now) {
|
|
141
|
+
const startTime = params.startTime ?? new Date(now() - (params.sinceMinutes ?? DEFAULT_SINCE_MINUTES) * 6e4).toISOString();
|
|
142
|
+
const result = await port.queryGcpLogs({
|
|
143
|
+
projectId: params.projectId,
|
|
144
|
+
env: params.env,
|
|
145
|
+
severity: params.severity,
|
|
146
|
+
services: params.services,
|
|
147
|
+
sqlInstances: params.sqlInstances,
|
|
148
|
+
allServices: params.allServices,
|
|
149
|
+
search: params.search,
|
|
150
|
+
filter: params.filter,
|
|
151
|
+
startTime,
|
|
152
|
+
endTime: params.endTime,
|
|
153
|
+
limit: params.limit,
|
|
154
|
+
pageToken: params.pageToken
|
|
155
|
+
});
|
|
156
|
+
if (result.error) return result.error;
|
|
157
|
+
const header = [
|
|
158
|
+
`env=${params.env ?? "prod"}`,
|
|
159
|
+
`window=${startTime}\u2192${params.endTime ?? "now"}`,
|
|
160
|
+
...params.severity ? [`minSeverity=${params.severity}`] : [],
|
|
161
|
+
`scope=${result.scopedServices ? `[${result.scopedServices.join(", ")}]` : "all"}`,
|
|
162
|
+
`entries=${result.entries.length}`
|
|
163
|
+
].join(" ");
|
|
164
|
+
const lines = result.entries.map(formatLogEntryLine);
|
|
165
|
+
const footer = result.nextPageToken ? [`-- more available: pass pageToken="${result.nextPageToken}" to continue`] : [];
|
|
166
|
+
if (lines.length === 0) {
|
|
167
|
+
return [
|
|
168
|
+
header,
|
|
169
|
+
"(no matching log entries \u2014 widen the window, lower minSeverity, or drop filters)"
|
|
170
|
+
].join("\n");
|
|
171
|
+
}
|
|
172
|
+
return [header, ...lines, ...footer].join("\n");
|
|
173
|
+
}
|
|
174
|
+
async function runQueryGrafanaLogs(port, params, now = Date.now) {
|
|
175
|
+
const startTime = params.startTime ?? new Date(now() - (params.sinceMinutes ?? DEFAULT_SINCE_MINUTES) * 6e4).toISOString();
|
|
176
|
+
const result = await port.queryGrafanaLogs({
|
|
177
|
+
projectId: params.projectId,
|
|
178
|
+
env: params.env,
|
|
179
|
+
level: params.level,
|
|
180
|
+
services: params.services,
|
|
181
|
+
search: params.search,
|
|
182
|
+
logql: params.logql,
|
|
183
|
+
startTime,
|
|
184
|
+
endTime: params.endTime,
|
|
185
|
+
limit: params.limit
|
|
186
|
+
});
|
|
187
|
+
if (result.error) return result.error;
|
|
188
|
+
const header = [
|
|
189
|
+
`env=${params.env ?? "prod"}`,
|
|
190
|
+
`window=${startTime}\u2192${params.endTime ?? "now"}`,
|
|
191
|
+
...params.level ? [`minLevel=${params.level}`] : [],
|
|
192
|
+
...result.logql ? [`logql=${truncateLine(result.logql)}`] : [],
|
|
193
|
+
`entries=${result.entries.length}`
|
|
194
|
+
].join(" ");
|
|
195
|
+
const lines = result.entries.map(formatLogEntryLine);
|
|
196
|
+
const oldest = result.entries.map((e) => e.timestamp).sort()[0];
|
|
197
|
+
const footer = result.hasMore ? [`-- hit the limit: older lines exist \u2014 pass endTime="${oldest}" to page further back`] : [];
|
|
198
|
+
if (lines.length === 0) {
|
|
199
|
+
return [
|
|
200
|
+
header,
|
|
201
|
+
"(no matching log entries \u2014 widen the window, lower minLevel, or drop filters)"
|
|
202
|
+
].join("\n");
|
|
203
|
+
}
|
|
204
|
+
return [header, ...lines, ...footer].join("\n");
|
|
205
|
+
}
|
|
118
206
|
|
|
119
207
|
// ../shared/dist/tool-contracts/index.js
|
|
120
208
|
var f = {
|
|
@@ -179,15 +267,23 @@ function compileBase(z10, spec) {
|
|
|
179
267
|
return z10.object(compileShape(z10, spec.fields));
|
|
180
268
|
}
|
|
181
269
|
}
|
|
182
|
-
function
|
|
270
|
+
function descriptionOf(spec) {
|
|
271
|
+
if (spec.kind === "optional" || spec.kind === "nullable") return descriptionOf(spec.inner);
|
|
272
|
+
return spec.desc;
|
|
273
|
+
}
|
|
274
|
+
function compileUndescribed(z10, spec) {
|
|
183
275
|
if (spec.kind === "optional") {
|
|
184
|
-
return
|
|
276
|
+
return compileUndescribed(z10, spec.inner).optional();
|
|
185
277
|
}
|
|
186
278
|
if (spec.kind === "nullable") {
|
|
187
|
-
return
|
|
279
|
+
return compileUndescribed(z10, spec.inner).nullable();
|
|
188
280
|
}
|
|
189
|
-
|
|
190
|
-
|
|
281
|
+
return compileBase(z10, spec);
|
|
282
|
+
}
|
|
283
|
+
function compileField(z10, spec) {
|
|
284
|
+
const schema = compileUndescribed(z10, spec);
|
|
285
|
+
const desc = descriptionOf(spec);
|
|
286
|
+
return desc === void 0 ? schema : schema.describe(desc);
|
|
191
287
|
}
|
|
192
288
|
function compileShape(z10, fields) {
|
|
193
289
|
const shape = {};
|
|
@@ -380,14 +476,118 @@ var approveAndMergePrContract = defineToolContract({
|
|
|
380
476
|
}
|
|
381
477
|
}
|
|
382
478
|
});
|
|
479
|
+
var getConnectionContextContract = defineToolContract({
|
|
480
|
+
name: "get_connection_context",
|
|
481
|
+
agent: {
|
|
482
|
+
description: "Resolve WHAT this session is bound to \u2014 call this FIRST, before inferring anything from names or branch strings. Returns the current task (id, title, status, branch, baseBranch, whether it is a pack parent, and its parentTaskId when it is itself a child), its project (id, name), and the acting agent (id, model, mode, isAuto). This is the pod-side half of the same tool the external MCP surface exposes, so a shared skill can open with one call in both places; it reports no user account, because a pod is bound to a card rather than to a person's session.",
|
|
483
|
+
fields: {}
|
|
484
|
+
},
|
|
485
|
+
mcp: {
|
|
486
|
+
description: "Resolve WHO this connection is and WHAT project/board it points at \u2014 call this FIRST, before inferring anything from names. Returns the effective account, project, and board (sub-project) each with BOTH its immutable ID and its human-readable name/slug, the granted capabilities (read/create/update/chat/files/build), management URLs, and a one-line summary. Removes the ambiguity between a project's canonical name and a board label. Pass projectId to target a specific project; otherwise the configured default project is used.",
|
|
487
|
+
fields: {
|
|
488
|
+
projectId: mcpProjectId
|
|
489
|
+
}
|
|
490
|
+
}
|
|
491
|
+
});
|
|
383
492
|
var tasksContracts = [
|
|
384
493
|
getTaskContract,
|
|
385
494
|
postToChatContract,
|
|
386
495
|
readTaskChatContract,
|
|
387
496
|
listTagsContract,
|
|
388
497
|
searchTasksContract,
|
|
389
|
-
approveAndMergePrContract
|
|
498
|
+
approveAndMergePrContract,
|
|
499
|
+
getConnectionContextContract
|
|
500
|
+
];
|
|
501
|
+
var STATUS_ENUM = [
|
|
502
|
+
"Planning",
|
|
503
|
+
"Open",
|
|
504
|
+
"InProgress",
|
|
505
|
+
"ReviewPR",
|
|
506
|
+
"ReviewDev",
|
|
507
|
+
"ReviewLive",
|
|
508
|
+
"Complete",
|
|
509
|
+
"Cancelled"
|
|
390
510
|
];
|
|
511
|
+
var RISK_ENUM = ["critical", "high", "medium", "low"];
|
|
512
|
+
var GITHUB_BRANCH_MEANING = "Record the task's ACTUAL working branch (e.g. a locally-driven pack's branch cut at claim time, so identification never mints a competing name and pack-child merges settle against reality).";
|
|
513
|
+
var GITHUB_BRANCH_DESC = "Record the task's ACTUAL working branch (e.g. a locally-driven pack's branch cut at claim time, so identification never mints a competing name and pack-child merges settle against reality), or null to detach. The branch must already exist on origin, and a live workspace bound to a different branch rejects the write.";
|
|
514
|
+
var updateTaskContract = defineToolContract({
|
|
515
|
+
name: "update_task",
|
|
516
|
+
agent: {
|
|
517
|
+
description: "Update the CURRENT task's title, description, plan, githubBranch, or status \u2014 the one tool the shared Conveyor skills use for card writes, so the same skill text works here and in a local MCP session. Pass task_id ONLY with status, to move one of this card's children (claim it InProgress, promote it to Open); every other field always applies to the current task, because the server scopes plan/title writes to this session. Status rides the same path a human or a build uses, so watchdog and board semantics hold \u2014 for an emergency override that skips those, use force_update_task_status.",
|
|
518
|
+
fields: {
|
|
519
|
+
title: f.optional(f.string({ desc: "New title" })),
|
|
520
|
+
description: f.optional(f.string({ desc: cardDescriptionDesc("New description") })),
|
|
521
|
+
plan: f.optional(f.string({ desc: "New plan (markdown)" })),
|
|
522
|
+
status: f.optional(f.enum(STATUS_ENUM, { desc: "New status" })),
|
|
523
|
+
// Not nullable here, unlike the mcp surface: the pod's underlying
|
|
524
|
+
// `updateTaskProperties` takes `githubBranch?: string` with no null
|
|
525
|
+
// branch, so a pod can RECORD a branch but cannot detach one. Offering
|
|
526
|
+
// null would advertise a write the server would silently drop.
|
|
527
|
+
githubBranch: f.optional(
|
|
528
|
+
f.string({
|
|
529
|
+
min: 1,
|
|
530
|
+
desc: `${GITHUB_BRANCH_MEANING} The pod does NOT validate this: the value is written through with no check that the ref exists on origin, and no guard against repointing the record away from the branch this pod is actually running on (which strands the compute). Push the branch first, and only name a branch this session genuinely owns. Detaching (clearing) a branch is not available from a pod \u2014 use the external MCP surface for that.`
|
|
531
|
+
})
|
|
532
|
+
),
|
|
533
|
+
task_id: f.optional(
|
|
534
|
+
f.string({
|
|
535
|
+
// min:1 is load-bearing: "" is falsy, so an empty task_id would slip
|
|
536
|
+
// past both the refusal below and the child-routing branch, and
|
|
537
|
+
// silently update the CURRENT card — the exact mis-targeting this
|
|
538
|
+
// field's guard exists to prevent.
|
|
539
|
+
min: 1,
|
|
540
|
+
desc: "Child task ID to move. Valid ONLY alongside status \u2014 pairing it with title/description/plan/githubBranch is rejected rather than silently applied to the current card. Omit to update the current task."
|
|
541
|
+
})
|
|
542
|
+
)
|
|
543
|
+
}
|
|
544
|
+
},
|
|
545
|
+
mcp: {
|
|
546
|
+
description: "Update task fields: title, description, plan, status, risk, story points, assignment, githubBranch, or tags. Set status to claim a card (InProgress), triage it (Open), or cancel it (Cancelled); for review approvals prefer approve_task / request_changes, which guard against stale-state races. Tags are additive/subtractive \u2014 pass addTags/removeTags with tag names (not a replace-set). Pass projectId to target a specific project; otherwise the configured default project is used. Moving a task beyond Planning auto-fills any missing icon, story points, and agent assignment \u2014 don't spend turns on them; pass storyPointValue only to correct the sizing yourself. For subtasks use update_subtask.",
|
|
547
|
+
fields: {
|
|
548
|
+
projectId: mcpProjectId,
|
|
549
|
+
taskId: f.string({ desc: "The task ID" }),
|
|
550
|
+
title: f.optional(f.string({ desc: "New title" })),
|
|
551
|
+
description: f.optional(f.string({ desc: cardDescriptionDesc("New description") })),
|
|
552
|
+
plan: f.optional(f.string({ desc: "New plan (markdown)" })),
|
|
553
|
+
status: f.optional(f.enum(STATUS_ENUM, { desc: "New status" })),
|
|
554
|
+
risk: f.optional(
|
|
555
|
+
f.nullable(
|
|
556
|
+
f.enum(RISK_ENUM, {
|
|
557
|
+
desc: "Risk level \u2014 how much important surface the task touches (critical/high/medium/low). Pass null to clear."
|
|
558
|
+
})
|
|
559
|
+
)
|
|
560
|
+
),
|
|
561
|
+
storyPointValue: f.optional(
|
|
562
|
+
f.nullable(
|
|
563
|
+
f.number({
|
|
564
|
+
desc: `${storyPointValueDesc}. The tiers are per-project \u2014 a value the project has not configured is rejected. Pass null to clear it; a card beyond Planning with no story point is re-filled by identification.`
|
|
565
|
+
})
|
|
566
|
+
)
|
|
567
|
+
),
|
|
568
|
+
assignedUserId: f.optional(f.nullable(f.string({ desc: "User ID to assign, or null" }))),
|
|
569
|
+
subProjectId: f.optional(
|
|
570
|
+
f.nullable(
|
|
571
|
+
f.string({
|
|
572
|
+
desc: "Assign the task to a sub-project board; null moves it back to the parent board. Omit to leave unchanged."
|
|
573
|
+
})
|
|
574
|
+
)
|
|
575
|
+
),
|
|
576
|
+
githubBranch: f.optional(f.nullable(f.string({ desc: GITHUB_BRANCH_DESC }))),
|
|
577
|
+
addTags: f.optional(
|
|
578
|
+
f.array(f.string(), {
|
|
579
|
+
desc: 'Tag names to add to the card (e.g. ["refactor"]). Additive \u2014 existing tags are kept. Unknown names are rejected; use list_tags to see available tags or manage_tags to create one.'
|
|
580
|
+
})
|
|
581
|
+
),
|
|
582
|
+
removeTags: f.optional(
|
|
583
|
+
f.array(f.string(), {
|
|
584
|
+
desc: "Tag names to remove from the card. Removing a tag the card doesn't have is a no-op."
|
|
585
|
+
})
|
|
586
|
+
)
|
|
587
|
+
}
|
|
588
|
+
}
|
|
589
|
+
});
|
|
590
|
+
var taskUpdateContracts = [updateTaskContract];
|
|
391
591
|
var tagRef = f.string({
|
|
392
592
|
desc: "Tag id, or the exact tag name (case-insensitive)",
|
|
393
593
|
min: 1,
|
|
@@ -678,7 +878,7 @@ var createSubtaskContract = defineToolContract({
|
|
|
678
878
|
var updateSubtaskContract = defineToolContract({
|
|
679
879
|
name: "update_subtask",
|
|
680
880
|
agent: {
|
|
681
|
-
description: "Update an existing subtask's fields (title, description, plan, ordinal, storyPointValue, dependsOn) \u2014 and the sanctioned path to make a child buildable: promote it to status Open, assign its agent (agentIdOrName), and set story points. Setting story points does NOT auto-promote; set status explicitly. For the current task use
|
|
881
|
+
description: "Update an existing subtask's fields (title, description, plan, ordinal, storyPointValue, dependsOn) \u2014 and the sanctioned path to make a child buildable: promote it to status Open, assign its agent (agentIdOrName), and set story points. Setting story points does NOT auto-promote; set status explicitly. For the current task use update_task.",
|
|
682
882
|
fields: {
|
|
683
883
|
subtaskId: f.string({ desc: "The subtask ID to update" }),
|
|
684
884
|
title: f.optional(f.string()),
|
|
@@ -743,7 +943,7 @@ var deleteSubtaskContract = defineToolContract({
|
|
|
743
943
|
var listSubtasksContract = defineToolContract({
|
|
744
944
|
name: "list_subtasks",
|
|
745
945
|
agent: {
|
|
746
|
-
description: "List all subtasks under the current parent task. Default compact view returns per child: status, agent, story points, PR number/state, dependencies
|
|
946
|
+
description: "List all subtasks under the current parent task. Default compact view returns per child: status, agent, story points, PR number/state, and dependencies. On a FAN-OUT pack it also returns holdsBuildSlot per child plus a packSlots summary (in-flight environments vs the PACK_CHILD_LIMIT cap, and which children hold the slots); both are omitted on the default single-pod path, where one pod implements every child and nothing holds a slot. Use to coordinate child work; pass verbose:true only when you need full description/plan text (large). For non-child tasks use get_task.",
|
|
747
947
|
fields: {
|
|
748
948
|
verbose: f.optional(
|
|
749
949
|
f.boolean({
|
|
@@ -849,7 +1049,7 @@ var getAttachmentContract = defineToolContract({
|
|
|
849
1049
|
});
|
|
850
1050
|
var attachmentTags = f.optional(
|
|
851
1051
|
f.array(f.string(), {
|
|
852
|
-
desc: `Glossary tag names this file is a relevant example of, e.g.
|
|
1052
|
+
desc: `Glossary tag names this file is a relevant example of. MUST be a JSON array of quoted strings, e.g. ["ops-hub", "platform-support"] \u2014 bare unquoted words are invalid JSON and fail the whole call before it is parsed (21 fleet calls in one week died this way). Use it when the file shows a tagged entity in a particular state: the tag's page lists its recent tagged attachments, so a reader can see what the entity looks like across the app and spot visual changes over time. Names are matched case-insensitively within the project; a name that matches no tag is reported back and never fails the upload. Max 5.`
|
|
853
1053
|
})
|
|
854
1054
|
);
|
|
855
1055
|
var uploadAttachmentContract = defineToolContract({
|
|
@@ -964,34 +1164,441 @@ var createPullRequestContract = defineToolContract({
|
|
|
964
1164
|
}
|
|
965
1165
|
});
|
|
966
1166
|
var pullRequestContracts = [createPullRequestContract];
|
|
967
|
-
var
|
|
968
|
-
|
|
969
|
-
|
|
970
|
-
|
|
971
|
-
|
|
972
|
-
|
|
973
|
-
|
|
974
|
-
|
|
975
|
-
|
|
976
|
-
|
|
977
|
-
|
|
978
|
-
|
|
979
|
-
|
|
980
|
-
|
|
981
|
-
|
|
982
|
-
|
|
983
|
-
|
|
984
|
-
|
|
985
|
-
|
|
986
|
-
|
|
987
|
-
|
|
988
|
-
|
|
989
|
-
|
|
990
|
-
|
|
991
|
-
|
|
992
|
-
|
|
993
|
-
|
|
994
|
-
|
|
1167
|
+
var REGISTRY_RULE = "Only channels an admin registered as work channels in project settings are reachable \u2014 an unregistered channel is invisible here no matter what the bot can see in the workspace, and the card-sync channels are NOT automatically included.";
|
|
1168
|
+
var NO_STORAGE = "Messages are fetched live from Slack/Discord on every call and are never stored by Conveyor.";
|
|
1169
|
+
var listProjectIntegrationsContract = defineToolContract({
|
|
1170
|
+
name: "list_project_integrations",
|
|
1171
|
+
agent: {
|
|
1172
|
+
description: `Find out what this project is connected to before assuming a capability exists \u2014 repository forge, Slack or Discord, email, GCP, Grafana, Google Analytics, Google Drive, Cloudflare, and whether incident reporting is set up. Also lists the registered work channels you may read or post in, and how many meetings have been ingested \u2014 so you can tell whether reading meetings is worth doing before calling a meeting tool. Credential-free: every value says whether something is configured, never what the secret is. Call this when you are about to reach for an integration and want to know whether it is there, rather than trying and handling a failure.`,
|
|
1173
|
+
fields: {}
|
|
1174
|
+
},
|
|
1175
|
+
mcp: {
|
|
1176
|
+
description: `Report which integrations a project has configured \u2014 repository forge, Slack/Discord, email, GCP, Grafana, Google Analytics, Google Drive, Cloudflare, incident reporting \u2014 plus the work channels registered for agent access and the project's ingested-meeting count. Returns configuration booleans only, never credential material. Pass projectId to target a specific project; otherwise the configured default project is used.`,
|
|
1177
|
+
fields: {
|
|
1178
|
+
projectId: mcpProjectId
|
|
1179
|
+
}
|
|
1180
|
+
}
|
|
1181
|
+
});
|
|
1182
|
+
var listProjectChannelsContract = defineToolContract({
|
|
1183
|
+
name: "list_project_channels",
|
|
1184
|
+
agent: {
|
|
1185
|
+
description: `List the Slack or Discord channels this project has registered as work channels, each with the admin's description of what happens there and whether you may read it and post into it. Start here before read_channel_messages \u2014 the description is what tells you which channel is worth consulting. ${REGISTRY_RULE}`,
|
|
1186
|
+
fields: {}
|
|
1187
|
+
},
|
|
1188
|
+
mcp: {
|
|
1189
|
+
description: `List a project's registered work channels: provider, channel id and name, the admin's description of what the channel is for, and the allowRead/allowPost grants. ${REGISTRY_RULE} Pass projectId to target a specific project; otherwise the configured default project is used.`,
|
|
1190
|
+
fields: {
|
|
1191
|
+
projectId: mcpProjectId
|
|
1192
|
+
}
|
|
1193
|
+
}
|
|
1194
|
+
});
|
|
1195
|
+
var CHANNEL_ID_DESC = "Provider channel id, exactly as list_project_channels reports it (Slack `C\u2026`, Discord a numeric id). Not the channel name.";
|
|
1196
|
+
var LIMIT_DESC = "How many messages to return, newest first. Defaults to 25, maximum 50. Prefer a small read plus a follow-up over one large one \u2014 channel history spends context fast.";
|
|
1197
|
+
var BEFORE_DESC = "Page further back: return messages older than this cursor. Pass the `olderCursor` from a previous call; a null cursor there means you have reached the start of the channel.";
|
|
1198
|
+
var AFTER_DESC = "Return only messages newer than this cursor. Use to catch up on a channel.";
|
|
1199
|
+
var THREAD_DESC = "Read one thread instead of the channel surface. Pass a `threadTs` seen on a message from a previous read.";
|
|
1200
|
+
var readChannelMessagesContract = defineToolContract({
|
|
1201
|
+
name: "read_channel_messages",
|
|
1202
|
+
agent: {
|
|
1203
|
+
description: `Read recent messages from a registered work channel, newest first \u2014 to catch up on a discussion, find the context behind a decision, or check whether something was already raised. Each message reports its author, whether that author is a bot (Conveyor's own card feed posts show up here too, so check this before treating a message as a teammate's), its text, and a cursor for paging further back. ${REGISTRY_RULE} ${NO_STORAGE}`,
|
|
1204
|
+
fields: {
|
|
1205
|
+
channelId: f.string({ desc: CHANNEL_ID_DESC }),
|
|
1206
|
+
limit: f.optional(f.number({ desc: LIMIT_DESC, int: true, min: 1, max: 50 })),
|
|
1207
|
+
before: f.optional(f.string({ desc: BEFORE_DESC })),
|
|
1208
|
+
after: f.optional(f.string({ desc: AFTER_DESC })),
|
|
1209
|
+
threadTs: f.optional(f.string({ desc: THREAD_DESC }))
|
|
1210
|
+
}
|
|
1211
|
+
},
|
|
1212
|
+
mcp: {
|
|
1213
|
+
description: `Read recent messages from a project's registered work channel, newest first. Each message carries author name, a bot flag, text, a provider-native cursor, and a timestamp; the response carries an olderCursor for paging back. ${REGISTRY_RULE} ${NO_STORAGE} Pass projectId to target a specific project; otherwise the configured default project is used.`,
|
|
1214
|
+
fields: {
|
|
1215
|
+
projectId: mcpProjectId,
|
|
1216
|
+
channelId: f.string({ desc: CHANNEL_ID_DESC }),
|
|
1217
|
+
limit: f.optional(f.number({ desc: LIMIT_DESC, int: true, min: 1, max: 50 })),
|
|
1218
|
+
before: f.optional(f.string({ desc: BEFORE_DESC })),
|
|
1219
|
+
after: f.optional(f.string({ desc: AFTER_DESC })),
|
|
1220
|
+
threadTs: f.optional(f.string({ desc: THREAD_DESC }))
|
|
1221
|
+
}
|
|
1222
|
+
}
|
|
1223
|
+
});
|
|
1224
|
+
var POST_TEXT_DESC = "What to say, in plain text or light markdown. An attribution footer naming you is appended automatically, so do not sign the message yourself. Channel-wide pings (@everyone, @here, <!channel>) are rendered as ordinary text and will not notify anyone.";
|
|
1225
|
+
var POST_THREAD_DESC = "Reply inside a thread rather than to the channel. Prefer this when responding to a specific message \u2014 pass the `threadTs` from the message you are answering.";
|
|
1226
|
+
var POST_WARNING = "This is visible to everyone in the channel and cannot be edited or deleted afterwards. Posting requires a per-channel grant that is OFF by default, separate from read access, so most registered channels will refuse it.";
|
|
1227
|
+
var postChannelMessageContract = defineToolContract({
|
|
1228
|
+
name: "post_channel_message",
|
|
1229
|
+
agent: {
|
|
1230
|
+
description: `Post a message into a registered work channel \u2014 to answer a question aimed at you, report something the team is waiting on, or ask for input you genuinely need. ${POST_WARNING} ${REGISTRY_RULE}`,
|
|
1231
|
+
fields: {
|
|
1232
|
+
channelId: f.string({ desc: CHANNEL_ID_DESC }),
|
|
1233
|
+
text: f.string({ desc: POST_TEXT_DESC, min: 1, max: 1800 }),
|
|
1234
|
+
threadTs: f.optional(f.string({ desc: POST_THREAD_DESC }))
|
|
1235
|
+
}
|
|
1236
|
+
},
|
|
1237
|
+
mcp: {
|
|
1238
|
+
description: `Post a message into a project's registered work channel, attributed to the acting agent or user. ${POST_WARNING} ${REGISTRY_RULE} Pass projectId to target a specific project; otherwise the configured default project is used.`,
|
|
1239
|
+
fields: {
|
|
1240
|
+
projectId: mcpProjectId,
|
|
1241
|
+
channelId: f.string({ desc: CHANNEL_ID_DESC }),
|
|
1242
|
+
text: f.string({ desc: POST_TEXT_DESC, min: 1, max: 1800 }),
|
|
1243
|
+
threadTs: f.optional(f.string({ desc: POST_THREAD_DESC }))
|
|
1244
|
+
}
|
|
1245
|
+
}
|
|
1246
|
+
});
|
|
1247
|
+
var ANALYTICS_RANGE = "How many days back to summarize, ending today. Defaults to 28; 90 is the maximum, because GA4 retention makes longer windows unreliable.";
|
|
1248
|
+
var ANALYTICS_CAMPAIGN = "Restrict to one campaign name. The campaigns breakdown is always unfiltered, so read it first to see what exists.";
|
|
1249
|
+
var ANALYTICS_SHAPE = "Returns headline totals (sessions, active users, new users, page views, average session duration, bounce rate) plus the top five pages, traffic sources, landing pages and campaigns. A project that has not set up GA4 answers configured:false with a reason rather than failing \u2014 that is a normal state, not an error.";
|
|
1250
|
+
var getAnalyticsSummaryContract = defineToolContract({
|
|
1251
|
+
name: "get_analytics_summary",
|
|
1252
|
+
agent: {
|
|
1253
|
+
description: `Read this project's Google Analytics 4 traffic \u2014 real numbers for questions like "did that launch land" or "which pages actually get read", instead of guessing. ${ANALYTICS_SHAPE}`,
|
|
1254
|
+
fields: {
|
|
1255
|
+
rangeDays: f.optional(f.number({ desc: ANALYTICS_RANGE, int: true, min: 1, max: 90 })),
|
|
1256
|
+
campaign: f.optional(f.string({ desc: ANALYTICS_CAMPAIGN, max: 200 }))
|
|
1257
|
+
}
|
|
1258
|
+
},
|
|
1259
|
+
mcp: {
|
|
1260
|
+
description: `Read a project's Google Analytics 4 traffic summary. ${ANALYTICS_SHAPE} Pass projectId to target a specific project; otherwise the configured default project is used.`,
|
|
1261
|
+
fields: {
|
|
1262
|
+
projectId: mcpProjectId,
|
|
1263
|
+
rangeDays: f.optional(f.number({ desc: ANALYTICS_RANGE, int: true, min: 1, max: 90 })),
|
|
1264
|
+
campaign: f.optional(f.string({ desc: ANALYTICS_CAMPAIGN, max: 200 }))
|
|
1265
|
+
}
|
|
1266
|
+
}
|
|
1267
|
+
});
|
|
1268
|
+
var integrationsContracts = [
|
|
1269
|
+
listProjectIntegrationsContract,
|
|
1270
|
+
listProjectChannelsContract,
|
|
1271
|
+
readChannelMessagesContract,
|
|
1272
|
+
postChannelMessageContract,
|
|
1273
|
+
getAnalyticsSummaryContract
|
|
1274
|
+
];
|
|
1275
|
+
var MAX_CONTENT_CHARS = 1e6;
|
|
1276
|
+
var FILE_ID = "Drive file id, as returned by drive_list_files";
|
|
1277
|
+
var ROOT_DEFAULT = "Defaults to the project's connected root folder.";
|
|
1278
|
+
var MCP_PROJECT_TAIL = "Pass projectId to target a specific project; otherwise the configured default project is used.";
|
|
1279
|
+
var driveListFilesContract = defineToolContract({
|
|
1280
|
+
name: "drive_list_files",
|
|
1281
|
+
agent: {
|
|
1282
|
+
description: "List files and folders in the project's connected Google Drive folder. Omit folderId to list the project's root folder. Returns id, name, mimeType, size, and modified time for each entry.",
|
|
1283
|
+
fields: {
|
|
1284
|
+
folderId: f.optional(f.string({ desc: `Folder to list. ${ROOT_DEFAULT}` })),
|
|
1285
|
+
search: f.optional(f.string({ desc: "Only return names containing this text", max: 200 })),
|
|
1286
|
+
limit: f.optional(f.number({ desc: "Max entries (default 100)", int: true, min: 1, max: 200 }))
|
|
1287
|
+
}
|
|
1288
|
+
},
|
|
1289
|
+
mcp: {
|
|
1290
|
+
description: `List files and folders in a project's connected Google Drive folder. Omit folderId to list the project's root folder. Returns id, name, mimeType, size, and modified time for each entry. ${MCP_PROJECT_TAIL}`,
|
|
1291
|
+
fields: {
|
|
1292
|
+
projectId: mcpProjectId,
|
|
1293
|
+
folderId: f.optional(f.string({ desc: `Folder to list. ${ROOT_DEFAULT}` })),
|
|
1294
|
+
search: f.optional(f.string({ desc: "Only return names containing this text", max: 200 })),
|
|
1295
|
+
limit: f.optional(f.number({ desc: "Max entries (default 100)", int: true, min: 1, max: 200 }))
|
|
1296
|
+
}
|
|
1297
|
+
}
|
|
1298
|
+
});
|
|
1299
|
+
var driveReadFileContract = defineToolContract({
|
|
1300
|
+
name: "drive_read_file",
|
|
1301
|
+
agent: {
|
|
1302
|
+
description: "Read a file's text content from the project's connected Google Drive folder. Google Docs, Sheets, and Slides are exported to text automatically. Content over 100 KB is truncated.",
|
|
1303
|
+
fields: { fileId: f.string({ desc: FILE_ID }) }
|
|
1304
|
+
},
|
|
1305
|
+
mcp: {
|
|
1306
|
+
description: `Read a file's text content from a project's connected Google Drive folder. Google Docs, Sheets, and Slides are exported to text automatically. Content over 100 KB is truncated. ${MCP_PROJECT_TAIL}`,
|
|
1307
|
+
fields: { projectId: mcpProjectId, fileId: f.string({ desc: FILE_ID }) }
|
|
1308
|
+
}
|
|
1309
|
+
});
|
|
1310
|
+
var driveCreateFileContract = defineToolContract({
|
|
1311
|
+
name: "drive_create_file",
|
|
1312
|
+
agent: {
|
|
1313
|
+
description: "Create a new file in the project's connected Google Drive folder. Use drive_update_file to change an existing file instead.",
|
|
1314
|
+
fields: {
|
|
1315
|
+
name: f.string({ desc: "File name, without any path separators", min: 1, max: 255 }),
|
|
1316
|
+
content: f.string({ desc: "File content, UTF-8 text", max: MAX_CONTENT_CHARS }),
|
|
1317
|
+
mimeType: f.optional(f.string({ desc: "MIME type (default text/plain)" })),
|
|
1318
|
+
folderId: f.optional(f.string({ desc: `Destination folder. ${ROOT_DEFAULT}` }))
|
|
1319
|
+
}
|
|
1320
|
+
},
|
|
1321
|
+
mcp: {
|
|
1322
|
+
description: `Create a new file in a project's connected Google Drive folder. Use drive_update_file to change an existing file instead. ${MCP_PROJECT_TAIL}`,
|
|
1323
|
+
fields: {
|
|
1324
|
+
projectId: mcpProjectId,
|
|
1325
|
+
name: f.string({ desc: "File name, without any path separators", min: 1, max: 255 }),
|
|
1326
|
+
content: f.string({ desc: "File content, UTF-8 text", max: MAX_CONTENT_CHARS }),
|
|
1327
|
+
mimeType: f.optional(f.string({ desc: "MIME type (default text/plain)" })),
|
|
1328
|
+
folderId: f.optional(f.string({ desc: `Destination folder. ${ROOT_DEFAULT}` }))
|
|
1329
|
+
}
|
|
1330
|
+
}
|
|
1331
|
+
});
|
|
1332
|
+
var driveUpdateFileContract = defineToolContract({
|
|
1333
|
+
name: "drive_update_file",
|
|
1334
|
+
agent: {
|
|
1335
|
+
description: "Replace the content of an existing file in the project's connected Google Drive folder. This overwrites the whole file. Google-native documents cannot be overwritten.",
|
|
1336
|
+
fields: {
|
|
1337
|
+
fileId: f.string({ desc: FILE_ID }),
|
|
1338
|
+
content: f.string({ desc: "Replacement content, UTF-8 text", max: MAX_CONTENT_CHARS }),
|
|
1339
|
+
mimeType: f.optional(f.string({ desc: "MIME type (defaults to the file's current type)" }))
|
|
1340
|
+
}
|
|
1341
|
+
},
|
|
1342
|
+
mcp: {
|
|
1343
|
+
description: `Replace the content of an existing file in a project's connected Google Drive folder. This overwrites the whole file \u2014 read it first if you are editing rather than replacing. Google-native documents cannot be overwritten. ${MCP_PROJECT_TAIL}`,
|
|
1344
|
+
fields: {
|
|
1345
|
+
projectId: mcpProjectId,
|
|
1346
|
+
fileId: f.string({ desc: FILE_ID }),
|
|
1347
|
+
content: f.string({ desc: "Replacement content, UTF-8 text", max: MAX_CONTENT_CHARS }),
|
|
1348
|
+
mimeType: f.optional(f.string({ desc: "MIME type (defaults to the file's current type)" }))
|
|
1349
|
+
}
|
|
1350
|
+
}
|
|
1351
|
+
});
|
|
1352
|
+
var driveDeleteFileContract = defineToolContract({
|
|
1353
|
+
name: "drive_delete_file",
|
|
1354
|
+
agent: {
|
|
1355
|
+
description: "Move a file in the project's connected Google Drive folder to the Drive trash. The file is recoverable from the trash; it is never permanently deleted.",
|
|
1356
|
+
fields: { fileId: f.string({ desc: FILE_ID }) }
|
|
1357
|
+
},
|
|
1358
|
+
mcp: {
|
|
1359
|
+
description: `Move a file in a project's connected Google Drive folder to the Drive trash. The file is recoverable from the trash; it is never permanently deleted. ${MCP_PROJECT_TAIL}`,
|
|
1360
|
+
fields: { projectId: mcpProjectId, fileId: f.string({ desc: FILE_ID }) }
|
|
1361
|
+
}
|
|
1362
|
+
});
|
|
1363
|
+
var driveCreateFolderContract = defineToolContract({
|
|
1364
|
+
name: "drive_create_folder",
|
|
1365
|
+
agent: {
|
|
1366
|
+
description: "Create a folder inside the project's connected Google Drive folder.",
|
|
1367
|
+
fields: {
|
|
1368
|
+
name: f.string({ desc: "Folder name, without any path separators", min: 1, max: 255 }),
|
|
1369
|
+
folderId: f.optional(f.string({ desc: `Parent folder. ${ROOT_DEFAULT}` }))
|
|
1370
|
+
}
|
|
1371
|
+
},
|
|
1372
|
+
mcp: {
|
|
1373
|
+
description: `Create a folder inside a project's connected Google Drive folder. ${MCP_PROJECT_TAIL}`,
|
|
1374
|
+
fields: {
|
|
1375
|
+
projectId: mcpProjectId,
|
|
1376
|
+
name: f.string({ desc: "Folder name, without any path separators", min: 1, max: 255 }),
|
|
1377
|
+
folderId: f.optional(f.string({ desc: `Parent folder. ${ROOT_DEFAULT}` }))
|
|
1378
|
+
}
|
|
1379
|
+
}
|
|
1380
|
+
});
|
|
1381
|
+
var driveContracts = [
|
|
1382
|
+
driveListFilesContract,
|
|
1383
|
+
driveReadFileContract,
|
|
1384
|
+
driveCreateFileContract,
|
|
1385
|
+
driveUpdateFileContract,
|
|
1386
|
+
driveDeleteFileContract,
|
|
1387
|
+
driveCreateFolderContract
|
|
1388
|
+
];
|
|
1389
|
+
var MEETING_ID = "Meeting id, as returned by list_meetings.";
|
|
1390
|
+
var MCP_TAIL = "Pass projectId to target a specific project; otherwise the configured default project is used.";
|
|
1391
|
+
var SUMMARY_NOTE = 'A meeting whose summary is still being written reports status "processing" and a null summary \u2014 that is normal shortly after ingestion, not an error.';
|
|
1392
|
+
var listMeetingsContract = defineToolContract({
|
|
1393
|
+
name: "list_meetings",
|
|
1394
|
+
agent: {
|
|
1395
|
+
description: `List this project's meetings, newest first \u2014 each with its title, date, source, status, participants, and a short summary preview. Start here when you are asked about "the meeting", "what did we decide", or "what came out of that call". ${SUMMARY_NOTE}`,
|
|
1396
|
+
fields: {
|
|
1397
|
+
limit: f.optional(
|
|
1398
|
+
f.number({ desc: "How many to return (default 20, maximum 50).", int: true, min: 1, max: 50 })
|
|
1399
|
+
),
|
|
1400
|
+
search: f.optional(
|
|
1401
|
+
f.string({ desc: "Case-insensitive match on the meeting title.", max: 200 })
|
|
1402
|
+
)
|
|
1403
|
+
}
|
|
1404
|
+
},
|
|
1405
|
+
mcp: {
|
|
1406
|
+
description: `List a project's meetings, newest first, with title, date, source, status, participants, and a summary preview. ${SUMMARY_NOTE} ${MCP_TAIL}`,
|
|
1407
|
+
fields: {
|
|
1408
|
+
projectId: mcpProjectId,
|
|
1409
|
+
limit: f.optional(
|
|
1410
|
+
f.number({ desc: "How many to return (default 20, maximum 50).", int: true, min: 1, max: 50 })
|
|
1411
|
+
),
|
|
1412
|
+
search: f.optional(
|
|
1413
|
+
f.string({ desc: "Case-insensitive match on the meeting title.", max: 200 })
|
|
1414
|
+
)
|
|
1415
|
+
}
|
|
1416
|
+
}
|
|
1417
|
+
});
|
|
1418
|
+
var getMeetingContract = defineToolContract({
|
|
1419
|
+
name: "get_meeting",
|
|
1420
|
+
agent: {
|
|
1421
|
+
description: `Read one meeting's details and its FULL AI summary \u2014 the overview, the decisions, and the proposed next steps. **Read this before reaching for the transcript**: the summary usually answers the question, and a transcript costs far more context. Also returns the participant list, the segment count, and the meeting's web link. ${SUMMARY_NOTE}`,
|
|
1422
|
+
fields: { meetingId: f.string({ desc: MEETING_ID }) }
|
|
1423
|
+
},
|
|
1424
|
+
mcp: {
|
|
1425
|
+
description: `Read one meeting's details and full AI summary (overview, decisions, proposed next steps), plus participants, segment count, and its web link. Prefer this over the transcript \u2014 the summary usually answers the question at a fraction of the context. ${SUMMARY_NOTE} ${MCP_TAIL}`,
|
|
1426
|
+
fields: { projectId: mcpProjectId, meetingId: f.string({ desc: MEETING_ID }) }
|
|
1427
|
+
}
|
|
1428
|
+
});
|
|
1429
|
+
var OFFSET_DESC = "Segment index to start from (default 0). Pass the nextOffset from the previous page to continue.";
|
|
1430
|
+
var LIMIT_DESC2 = "Segments per page (default 200, maximum 500). A long meeting runs to thousands, so page it rather than asking for everything.";
|
|
1431
|
+
var readMeetingTranscriptContract = defineToolContract({
|
|
1432
|
+
name: "read_meeting_transcript",
|
|
1433
|
+
agent: {
|
|
1434
|
+
description: `Read a page of a meeting's raw transcript as \`[hh:mm:ss] Speaker: text\` lines. Use this when you need someone's exact words \u2014 a quote, a caveat, or the reasoning behind a decision the summary only states. For "what happened in the meeting", read get_meeting instead. The response carries nextOffset when more segments remain.`,
|
|
1435
|
+
fields: {
|
|
1436
|
+
meetingId: f.string({ desc: MEETING_ID }),
|
|
1437
|
+
offset: f.optional(f.number({ desc: OFFSET_DESC, int: true, min: 0 })),
|
|
1438
|
+
limit: f.optional(f.number({ desc: LIMIT_DESC2, int: true, min: 1, max: 500 }))
|
|
1439
|
+
}
|
|
1440
|
+
},
|
|
1441
|
+
mcp: {
|
|
1442
|
+
description: `Read a page of a meeting's raw transcript as \`[hh:mm:ss] Speaker: text\` lines. Use it for exact wording; prefer get_meeting's summary for what happened. The response carries nextOffset when more segments remain. ${MCP_TAIL}`,
|
|
1443
|
+
fields: {
|
|
1444
|
+
projectId: mcpProjectId,
|
|
1445
|
+
meetingId: f.string({ desc: MEETING_ID }),
|
|
1446
|
+
offset: f.optional(f.number({ desc: OFFSET_DESC, int: true, min: 0 })),
|
|
1447
|
+
limit: f.optional(f.number({ desc: LIMIT_DESC2, int: true, min: 1, max: 500 }))
|
|
1448
|
+
}
|
|
1449
|
+
}
|
|
1450
|
+
});
|
|
1451
|
+
var meetingsContracts = [
|
|
1452
|
+
listMeetingsContract,
|
|
1453
|
+
getMeetingContract,
|
|
1454
|
+
readMeetingTranscriptContract
|
|
1455
|
+
];
|
|
1456
|
+
var SINCE_MINUTES = f.optional(
|
|
1457
|
+
f.number({
|
|
1458
|
+
desc: "Relative time window ending now, in minutes (default 60). Ignored if startTime is set.",
|
|
1459
|
+
int: true,
|
|
1460
|
+
min: 1,
|
|
1461
|
+
max: 10080
|
|
1462
|
+
})
|
|
1463
|
+
);
|
|
1464
|
+
var START_TIME = f.optional(
|
|
1465
|
+
f.string({ desc: "ISO 8601 lower bound (overrides sinceMinutes)" })
|
|
1466
|
+
);
|
|
1467
|
+
var END_TIME = f.optional(f.string({ desc: "ISO 8601 upper bound (default now)" }));
|
|
1468
|
+
var LIMIT = f.optional(
|
|
1469
|
+
f.number({ desc: "Max entries per page (default 50)", int: true, min: 1, max: 200 })
|
|
1470
|
+
);
|
|
1471
|
+
var RAW_QUERY_MAX = 2e3;
|
|
1472
|
+
var gcpFields = {
|
|
1473
|
+
env: f.optional(
|
|
1474
|
+
f.enum(["prod", "dev", "claudespace"], {
|
|
1475
|
+
desc: "GCP environment slot to query (default prod)"
|
|
1476
|
+
})
|
|
1477
|
+
),
|
|
1478
|
+
sinceMinutes: SINCE_MINUTES,
|
|
1479
|
+
startTime: START_TIME,
|
|
1480
|
+
endTime: END_TIME,
|
|
1481
|
+
severity: f.optional(
|
|
1482
|
+
f.enum(SEVERITY_ENUM, {
|
|
1483
|
+
desc: "Minimum severity, inclusive \u2014 ERROR returns ERROR and above"
|
|
1484
|
+
})
|
|
1485
|
+
),
|
|
1486
|
+
services: f.optional(
|
|
1487
|
+
f.array(f.string(), {
|
|
1488
|
+
desc: "Restrict to these Cloud Run service names (prod/dev only). Defaults to all services linked in project settings."
|
|
1489
|
+
})
|
|
1490
|
+
),
|
|
1491
|
+
sqlInstances: f.optional(
|
|
1492
|
+
f.array(f.string(), { desc: "Restrict to these Cloud SQL instance names (prod/dev only)" })
|
|
1493
|
+
),
|
|
1494
|
+
allServices: f.optional(
|
|
1495
|
+
f.boolean({
|
|
1496
|
+
desc: "Set true to search ALL logs in the GCP project, ignoring the linked-resource scope"
|
|
1497
|
+
})
|
|
1498
|
+
),
|
|
1499
|
+
search: f.optional(
|
|
1500
|
+
f.string({ desc: "Free-text search across all log fields (exact substring, not regex)", max: 256 })
|
|
1501
|
+
),
|
|
1502
|
+
filter: f.optional(
|
|
1503
|
+
f.string({
|
|
1504
|
+
desc: "Advanced: raw Cloud Logging filter expression, ANDed with the scope (it cannot widen it)",
|
|
1505
|
+
max: RAW_QUERY_MAX
|
|
1506
|
+
})
|
|
1507
|
+
),
|
|
1508
|
+
limit: LIMIT,
|
|
1509
|
+
pageToken: f.optional(
|
|
1510
|
+
f.string({ desc: "Opaque token from a previous response to fetch the next page" })
|
|
1511
|
+
)
|
|
1512
|
+
};
|
|
1513
|
+
var GCP_SHARED_DESC = "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.";
|
|
1514
|
+
var queryGcpLogsContract = defineToolContract({
|
|
1515
|
+
name: "query_gcp_logs",
|
|
1516
|
+
agent: {
|
|
1517
|
+
description: `Query Google Cloud Logging for this project's linked GCP environments \u2014 use this to investigate a production or dev failure directly rather than guessing from the code. ${GCP_SHARED_DESC}`,
|
|
1518
|
+
fields: gcpFields
|
|
1519
|
+
},
|
|
1520
|
+
mcp: {
|
|
1521
|
+
description: `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'). ${GCP_SHARED_DESC} Pass projectId to target a specific project; otherwise the configured default project is used.`,
|
|
1522
|
+
fields: { projectId: mcpProjectId, ...gcpFields }
|
|
1523
|
+
}
|
|
1524
|
+
});
|
|
1525
|
+
var grafanaFields = {
|
|
1526
|
+
env: f.optional(
|
|
1527
|
+
f.enum(["prod", "dev"], {
|
|
1528
|
+
desc: "Configured Grafana env mapping to scope by (default prod)"
|
|
1529
|
+
})
|
|
1530
|
+
),
|
|
1531
|
+
sinceMinutes: SINCE_MINUTES,
|
|
1532
|
+
startTime: START_TIME,
|
|
1533
|
+
endTime: END_TIME,
|
|
1534
|
+
level: f.optional(
|
|
1535
|
+
f.enum(["debug", "info", "warn", "error", "fatal"], {
|
|
1536
|
+
desc: "Minimum severity, inclusive \u2014 error returns error and above"
|
|
1537
|
+
})
|
|
1538
|
+
),
|
|
1539
|
+
services: f.optional(
|
|
1540
|
+
f.array(f.string(), { desc: "Restrict to these service_name label values" })
|
|
1541
|
+
),
|
|
1542
|
+
search: f.optional(
|
|
1543
|
+
f.string({ desc: "Substring line filter (exact substring, not regex)", max: 256 })
|
|
1544
|
+
),
|
|
1545
|
+
logql: f.optional(
|
|
1546
|
+
f.string({
|
|
1547
|
+
desc: "Advanced: raw LogQL query \u2014 REPLACES env/services/level/search composition",
|
|
1548
|
+
max: RAW_QUERY_MAX
|
|
1549
|
+
})
|
|
1550
|
+
),
|
|
1551
|
+
limit: LIMIT
|
|
1552
|
+
};
|
|
1553
|
+
var GRAFANA_SHARED_DESC = "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 cannot express the query (it REPLACES them). The response header echoes the composed LogQL \u2014 iterate on it. Returns compact lines: '<time> <SEVERITY> [<service>] <message>'.";
|
|
1554
|
+
var queryGrafanaLogsContract = defineToolContract({
|
|
1555
|
+
name: "query_grafana_logs",
|
|
1556
|
+
agent: {
|
|
1557
|
+
description: `Query this project's connected Grafana (Loki) logs \u2014 ${GRAFANA_SHARED_DESC}`,
|
|
1558
|
+
fields: grafanaFields
|
|
1559
|
+
},
|
|
1560
|
+
mcp: {
|
|
1561
|
+
description: `Query the project's connected Grafana (Loki) logs \u2014 ${GRAFANA_SHARED_DESC} Pass projectId to target a specific project; otherwise the configured default project is used.`,
|
|
1562
|
+
fields: { projectId: mcpProjectId, ...grafanaFields }
|
|
1563
|
+
}
|
|
1564
|
+
});
|
|
1565
|
+
var logsContracts = [
|
|
1566
|
+
queryGcpLogsContract,
|
|
1567
|
+
queryGrafanaLogsContract
|
|
1568
|
+
];
|
|
1569
|
+
var TOOL_CONTRACTS = Object.fromEntries(
|
|
1570
|
+
[
|
|
1571
|
+
...tasksContracts,
|
|
1572
|
+
...taskUpdateContracts,
|
|
1573
|
+
...tagsContracts,
|
|
1574
|
+
...checklistContracts,
|
|
1575
|
+
...dependenciesContracts,
|
|
1576
|
+
...subtasksContracts,
|
|
1577
|
+
...attachmentsContracts,
|
|
1578
|
+
...suggestionsContracts,
|
|
1579
|
+
...pullRequestContracts,
|
|
1580
|
+
...integrationsContracts,
|
|
1581
|
+
...driveContracts,
|
|
1582
|
+
...meetingsContracts,
|
|
1583
|
+
...logsContracts
|
|
1584
|
+
].map((contract) => [contract.name, contract])
|
|
1585
|
+
);
|
|
1586
|
+
|
|
1587
|
+
// src/tools/contract-tool.ts
|
|
1588
|
+
import { z as z2 } from "zod";
|
|
1589
|
+
function mcpShape(surface) {
|
|
1590
|
+
return compileShape(z2, surface.fields);
|
|
1591
|
+
}
|
|
1592
|
+
function registerContractTool(server2, contract, handler, options) {
|
|
1593
|
+
if (options?.alwaysLoad) {
|
|
1594
|
+
registerHotTool(
|
|
1595
|
+
server2,
|
|
1596
|
+
contract.name,
|
|
1597
|
+
contract.mcp.description,
|
|
1598
|
+
mcpShape(contract.mcp),
|
|
1599
|
+
handler
|
|
1600
|
+
);
|
|
1601
|
+
return;
|
|
995
1602
|
}
|
|
996
1603
|
server2.tool(
|
|
997
1604
|
contract.name,
|
|
@@ -1012,7 +1619,42 @@ function registerHotTool(server2, name, description, schema, handler) {
|
|
|
1012
1619
|
);
|
|
1013
1620
|
}
|
|
1014
1621
|
|
|
1622
|
+
// src/tools/connection.ts
|
|
1623
|
+
var CAPABILITY_ENUM = ["read", "create", "update", "chat", "files", "build"];
|
|
1624
|
+
function registerConnectionTools(server2, conn2) {
|
|
1625
|
+
registerContractTool(server2, getConnectionContextContract, async (params) => {
|
|
1626
|
+
const ctx = await conn2.getConnectionContext(params.projectId);
|
|
1627
|
+
return { content: [{ type: "text", text: JSON.stringify(ctx, null, 2) }] };
|
|
1628
|
+
});
|
|
1629
|
+
server2.tool(
|
|
1630
|
+
"verify_connection",
|
|
1631
|
+
"Prove the connection is correct AND that you can actually write to the intended board \u2014 not just that auth works. Runs layered checks (auth \u2192 account \u2192 project \u2192 target board \u2192 capabilities \u2192 read) and returns a plain pass/fail with, on failure, the exact failing layer and ONE next action. Use this instead of get_project_summary to confirm setup: a summary that returns data proves auth, not scope. Pass intendedActions to verify specific capabilities (defaults to read+create+update). Pass projectId to target a specific project; otherwise the configured default project is used. The board scope comes from CONVEYOR_SUBPROJECT_ID.",
|
|
1632
|
+
{
|
|
1633
|
+
projectId: z3.string().optional().describe("Target Conveyor project ID"),
|
|
1634
|
+
intendedActions: z3.array(z3.enum(CAPABILITY_ENUM)).optional().describe(
|
|
1635
|
+
"Capabilities to verify the connection can perform (default: read, create, update)."
|
|
1636
|
+
)
|
|
1637
|
+
},
|
|
1638
|
+
async (params) => {
|
|
1639
|
+
const result = await conn2.verifyConnection(params);
|
|
1640
|
+
return { content: [{ type: "text", text: JSON.stringify(result, null, 2) }] };
|
|
1641
|
+
}
|
|
1642
|
+
);
|
|
1643
|
+
server2.tool(
|
|
1644
|
+
"list_accessible_subprojects",
|
|
1645
|
+
"List the boards (sub-projects) under the connected project \u2014 each with its ID, name, slug, board URL, owned root path, and the role/capabilities this token has on it. Use this to discover which board to create or list cards on (pass a returned id as subProjectId, or set CONVEYOR_SUBPROJECT_ID) instead of asking the human for one. Pass projectId to target a specific project; otherwise the configured default project is used.",
|
|
1646
|
+
{
|
|
1647
|
+
projectId: z3.string().optional().describe("Target Conveyor project ID")
|
|
1648
|
+
},
|
|
1649
|
+
async (params) => {
|
|
1650
|
+
const subprojects = await conn2.listAccessibleSubprojects(params.projectId);
|
|
1651
|
+
return { content: [{ type: "text", text: JSON.stringify(subprojects, null, 2) }] };
|
|
1652
|
+
}
|
|
1653
|
+
);
|
|
1654
|
+
}
|
|
1655
|
+
|
|
1015
1656
|
// src/tools/project-config.ts
|
|
1657
|
+
import { z as z4 } from "zod";
|
|
1016
1658
|
var CONTEXT_LINK_LOCATOR_MAX = 300;
|
|
1017
1659
|
function jsonResult(data) {
|
|
1018
1660
|
return { content: [{ type: "text", text: JSON.stringify(data, null, 2) }] };
|
|
@@ -1333,7 +1975,7 @@ function summarizeTask(task) {
|
|
|
1333
1975
|
}
|
|
1334
1976
|
|
|
1335
1977
|
// src/tools/tasks.ts
|
|
1336
|
-
var
|
|
1978
|
+
var STATUS_ENUM2 = [
|
|
1337
1979
|
"Planning",
|
|
1338
1980
|
"Open",
|
|
1339
1981
|
"InProgress",
|
|
@@ -1344,7 +1986,7 @@ var STATUS_ENUM = [
|
|
|
1344
1986
|
"Cancelled"
|
|
1345
1987
|
];
|
|
1346
1988
|
var CARD_TYPE_ENUM = ["task", "incident", "suggestion"];
|
|
1347
|
-
var
|
|
1989
|
+
var RISK_ENUM2 = ["critical", "high", "medium", "low"];
|
|
1348
1990
|
var BOARD_FILTER = z5.string().nullable().optional().describe(
|
|
1349
1991
|
"Filter to a sub-project board. Omit to use the connection's default board (CONVEYOR_SUBPROJECT_ID) when set, else the whole project; pass null to force the whole project. Use list_accessible_subprojects to find board IDs."
|
|
1350
1992
|
);
|
|
@@ -1358,7 +2000,7 @@ function registerListTasks(server2, conn2) {
|
|
|
1358
2000
|
"List project cards, optionally filtered by card type, status, or assignment (a specific assignee, or unassigned tasks). Defaults to type=task \u2014 pass typeFilters to list incidents/suggestions. Results are relevance-ordered: highest priority first then newest; suggestions-only queries rank by upvote score. Pass projectId to target a specific project; otherwise the configured default project is used. Returns summaries \u2014 plan omitted, description truncated; use get_task for full details.",
|
|
1359
2001
|
{
|
|
1360
2002
|
projectId: z5.string().optional().describe("Target Conveyor project ID"),
|
|
1361
|
-
status: z5.enum(
|
|
2003
|
+
status: z5.enum(STATUS_ENUM2).optional().describe("Filter by task status"),
|
|
1362
2004
|
typeFilters: z5.array(z5.enum(CARD_TYPE_ENUM)).optional().describe(
|
|
1363
2005
|
'Card types to include, e.g. ["incident"] or ["task", "incident"]. Omit for tasks only.'
|
|
1364
2006
|
),
|
|
@@ -1446,51 +2088,20 @@ function registerCreateTask(server2, conn2) {
|
|
|
1446
2088
|
);
|
|
1447
2089
|
}
|
|
1448
2090
|
function registerUpdateTask(server2, conn2) {
|
|
1449
|
-
server2
|
|
1450
|
-
|
|
1451
|
-
|
|
1452
|
-
{
|
|
1453
|
-
|
|
1454
|
-
|
|
1455
|
-
title: z5.string().optional().describe("New title"),
|
|
1456
|
-
description: z5.string().optional().describe(cardDescriptionDesc("New description")),
|
|
1457
|
-
plan: z5.string().optional().describe("New plan (markdown)"),
|
|
1458
|
-
status: z5.enum(STATUS_ENUM).optional().describe("New status"),
|
|
1459
|
-
risk: z5.enum(RISK_ENUM).nullable().optional().describe(
|
|
1460
|
-
"Risk level \u2014 how much important surface the task touches (critical/high/medium/low). Pass null to clear."
|
|
1461
|
-
),
|
|
1462
|
-
storyPointValue: z5.number().nullable().optional().describe(
|
|
1463
|
-
`${storyPointValueDesc}. The tiers are per-project \u2014 a value the project has not configured is rejected. Pass null to clear it; a card beyond Planning with no story point is re-filled by identification.`
|
|
1464
|
-
),
|
|
1465
|
-
assignedUserId: z5.string().nullable().optional().describe("User ID to assign, or null"),
|
|
1466
|
-
subProjectId: z5.string().nullable().optional().describe(
|
|
1467
|
-
"Assign the task to a sub-project board; null moves it back to the parent board. Omit to leave unchanged."
|
|
1468
|
-
),
|
|
1469
|
-
githubBranch: z5.string().nullable().optional().describe(
|
|
1470
|
-
"Record the task's ACTUAL working branch (e.g. a locally-driven pack's branch cut at claim time, so identification never mints a competing name and pack-child merges settle against reality), or null to detach. The branch must already exist on origin, and a live workspace bound to a different branch rejects the write."
|
|
1471
|
-
),
|
|
1472
|
-
addTags: z5.array(z5.string()).optional().describe(
|
|
1473
|
-
'Tag names to add to the card (e.g. ["refactor"]). Additive \u2014 existing tags are kept. Unknown names are rejected; use list_tags to see available tags or manage_tags to create one.'
|
|
1474
|
-
),
|
|
1475
|
-
removeTags: z5.array(z5.string()).optional().describe(
|
|
1476
|
-
"Tag names to remove from the card. Removing a tag the card doesn't have is a no-op."
|
|
1477
|
-
)
|
|
1478
|
-
},
|
|
1479
|
-
async (params) => {
|
|
1480
|
-
const result = await conn2.updateTask(params);
|
|
1481
|
-
const parts = [`Task ${result.id} updated`];
|
|
1482
|
-
if (result.status) parts.push(`status: ${result.status}`);
|
|
1483
|
-
if (params.storyPointValue !== void 0) {
|
|
1484
|
-
parts.push(`story points: ${result.storyPointValue ?? "cleared"}`);
|
|
1485
|
-
}
|
|
1486
|
-
if ((result.addedTags ?? []).length > 0) parts.push(`+tags: ${result.addedTags.join(", ")}`);
|
|
1487
|
-
if ((result.removedTags ?? []).length > 0)
|
|
1488
|
-
parts.push(`-tags: ${result.removedTags.join(", ")}`);
|
|
1489
|
-
return {
|
|
1490
|
-
content: [{ type: "text", text: parts.join(" \xB7 ") }]
|
|
1491
|
-
};
|
|
2091
|
+
registerContractTool(server2, updateTaskContract, async (params) => {
|
|
2092
|
+
const result = await conn2.updateTask(params);
|
|
2093
|
+
const parts = [`Task ${result.id} updated`];
|
|
2094
|
+
if (result.status) parts.push(`status: ${result.status}`);
|
|
2095
|
+
if (params.storyPointValue !== void 0) {
|
|
2096
|
+
parts.push(`story points: ${result.storyPointValue ?? "cleared"}`);
|
|
1492
2097
|
}
|
|
1493
|
-
|
|
2098
|
+
if ((result.addedTags ?? []).length > 0) parts.push(`+tags: ${result.addedTags.join(", ")}`);
|
|
2099
|
+
if ((result.removedTags ?? []).length > 0)
|
|
2100
|
+
parts.push(`-tags: ${result.removedTags.join(", ")}`);
|
|
2101
|
+
return {
|
|
2102
|
+
content: [{ type: "text", text: parts.join(" \xB7 ") }]
|
|
2103
|
+
};
|
|
2104
|
+
});
|
|
1494
2105
|
}
|
|
1495
2106
|
function registerMoveCard(server2, conn2) {
|
|
1496
2107
|
server2.tool(
|
|
@@ -1613,7 +2224,7 @@ function registerReviewTools(server2, conn2) {
|
|
|
1613
2224
|
{
|
|
1614
2225
|
projectId: z5.string().optional().describe("Target Conveyor project ID"),
|
|
1615
2226
|
taskId: z5.string().describe("The task ID"),
|
|
1616
|
-
risk: z5.enum(
|
|
2227
|
+
risk: z5.enum(RISK_ENUM2).optional().describe("Optional risk level to record; defaults to a raise-only 'low' on approval.")
|
|
1617
2228
|
},
|
|
1618
2229
|
async (params) => {
|
|
1619
2230
|
const result = await conn2.approveTask(params.taskId, params.projectId, params.risk);
|
|
@@ -1634,7 +2245,7 @@ function registerReviewTools(server2, conn2) {
|
|
|
1634
2245
|
projectId: z5.string().optional().describe("Target Conveyor project ID"),
|
|
1635
2246
|
taskId: z5.string().describe("The task ID"),
|
|
1636
2247
|
feedback: z5.string().describe("Feedback message describing requested changes"),
|
|
1637
|
-
risk: z5.enum(
|
|
2248
|
+
risk: z5.enum(RISK_ENUM2).optional().describe("Optional risk level to record; defaults to a raise-only 'medium' on changes.")
|
|
1638
2249
|
},
|
|
1639
2250
|
async (params) => {
|
|
1640
2251
|
await conn2.requestChanges(params.taskId, params.feedback, params.projectId, params.risk);
|
|
@@ -2426,193 +3037,2071 @@ function registerWorkspaceTools(server2, conn2, deps = {}) {
|
|
|
2426
3037
|
registerStopTunnelTool(server2);
|
|
2427
3038
|
}
|
|
2428
3039
|
|
|
2429
|
-
//
|
|
3040
|
+
// ../shared/dist/index.js
|
|
2430
3041
|
import { z as z9 } from "zod";
|
|
2431
|
-
|
|
2432
|
-
|
|
2433
|
-
|
|
2434
|
-
|
|
2435
|
-
|
|
2436
|
-
|
|
2437
|
-
|
|
2438
|
-
|
|
2439
|
-
|
|
3042
|
+
import { z as z22 } from "zod";
|
|
3043
|
+
import { z as z32 } from "zod";
|
|
3044
|
+
import { z as z42 } from "zod";
|
|
3045
|
+
import { z as z52 } from "zod";
|
|
3046
|
+
import { z as z62 } from "zod";
|
|
3047
|
+
import { z as z72 } from "zod";
|
|
3048
|
+
import { z as z82 } from "zod";
|
|
3049
|
+
import { z as z92 } from "zod";
|
|
3050
|
+
var DEFAULT_SONNET_MODEL = "claude-sonnet-5";
|
|
3051
|
+
var DEFAULT_OPUS_MODEL = "claude-opus-5";
|
|
3052
|
+
var DEFAULT_HAIKU_MODEL = "claude-haiku-4-5-20251001";
|
|
3053
|
+
var FABLE_MODEL = "claude-fable-5";
|
|
3054
|
+
var PREVIOUS_SONNET_MODEL = "claude-sonnet-4-6";
|
|
3055
|
+
var PREVIOUS_OPUS_MODEL = "claude-opus-4-8";
|
|
3056
|
+
var PTY_STREAM_PORT_BASE = 7420;
|
|
3057
|
+
var PTY_STREAM_PORT_ATTEMPTS = 8;
|
|
3058
|
+
var PREVIEW_PORT_DENY_LIST = [
|
|
3059
|
+
5432,
|
|
3060
|
+
6379,
|
|
3061
|
+
9200,
|
|
3062
|
+
...Array.from({ length: PTY_STREAM_PORT_ATTEMPTS }, (_, i) => PTY_STREAM_PORT_BASE + i)
|
|
2440
3063
|
];
|
|
2441
|
-
|
|
2442
|
-
|
|
2443
|
-
|
|
2444
|
-
|
|
2445
|
-
|
|
2446
|
-
const overflow = oneLine.length - MAX_LINE_CHARS;
|
|
2447
|
-
return `${oneLine.slice(0, MAX_LINE_CHARS)}\u2026[+${overflow}c]`;
|
|
3064
|
+
function normalizeCheckpointPath(value) {
|
|
3065
|
+
let normalized = value.trim().replace(/\/{2,}/g, "/");
|
|
3066
|
+
normalized = normalized.split("/").filter((segment) => segment !== ".").join("/");
|
|
3067
|
+
while (normalized.endsWith("/")) normalized = normalized.slice(0, -1);
|
|
3068
|
+
return normalized;
|
|
2448
3069
|
}
|
|
2449
|
-
|
|
2450
|
-
|
|
3070
|
+
var checkpointPathSchema = z9.string().transform(normalizeCheckpointPath).pipe(
|
|
3071
|
+
z9.string().min(1).refine((value) => value !== ".", "Checkpoint paths must name a repository entry").refine((value) => !value.startsWith("/"), "Checkpoint paths must be repository-relative").refine(
|
|
3072
|
+
(value) => !/^[A-Za-z]:[\\/]/.test(value) && !value.includes("\\"),
|
|
3073
|
+
"Checkpoint paths must use repository-relative POSIX syntax"
|
|
3074
|
+
).refine(
|
|
3075
|
+
(value) => !value.split("/").includes(".."),
|
|
3076
|
+
"Checkpoint paths must not traverse a parent directory"
|
|
3077
|
+
)
|
|
3078
|
+
);
|
|
3079
|
+
var secretNameSchema = z9.string().regex(/^[A-Za-z_][A-Za-z0-9_]*$/);
|
|
3080
|
+
var checkpointKeySchema = z9.string().regex(/^[0-9a-f]{64}$/);
|
|
3081
|
+
var checkpointDigestRefSchema = z9.string().regex(/^[^\s@]+@sha256:[0-9a-f]{64}$/);
|
|
3082
|
+
var ACTIONS_PREBAKE_REGISTRY_PATTERN = /^[a-z0-9](?:[a-z0-9.-]*[a-z0-9])?(?::(?:[1-9][0-9]{0,4}))?(?:\/[a-z0-9]+(?:[._-][a-z0-9]+)*)*$/;
|
|
3083
|
+
var actionsPrebakeRegistrySchema = z9.string().trim().min(1).regex(
|
|
3084
|
+
ACTIONS_PREBAKE_REGISTRY_PATTERN,
|
|
3085
|
+
"Actions prebake registry must be a lowercase host[:port] with an optional path prefix"
|
|
3086
|
+
).refine((value) => {
|
|
3087
|
+
const port = /:([0-9]+)(?:\/|$)/.exec(value)?.[1];
|
|
3088
|
+
return !port || Number(port) <= 65535;
|
|
3089
|
+
}, "Actions prebake registry port must be between 1 and 65535");
|
|
3090
|
+
function uniqueSortedArray(item, minimum = 0) {
|
|
3091
|
+
return z9.array(item).min(minimum).superRefine((values, ctx) => {
|
|
3092
|
+
if (new Set(values).size !== values.length) {
|
|
3093
|
+
ctx.addIssue({ code: z9.ZodIssueCode.custom, message: "Duplicate values are not allowed" });
|
|
3094
|
+
}
|
|
3095
|
+
}).transform((values) => [...values].sort());
|
|
2451
3096
|
}
|
|
2452
|
-
var
|
|
2453
|
-
|
|
2454
|
-
|
|
2455
|
-
|
|
2456
|
-
|
|
2457
|
-
|
|
2458
|
-
|
|
2459
|
-
|
|
2460
|
-
|
|
2461
|
-
|
|
2462
|
-
|
|
2463
|
-
|
|
3097
|
+
var projectCheckpointSettingsSchema = z9.object({
|
|
3098
|
+
enabled: z9.literal(true),
|
|
3099
|
+
cacheCommand: z9.string().trim().min(1),
|
|
3100
|
+
cacheInputPaths: uniqueSortedArray(checkpointPathSchema, 1),
|
|
3101
|
+
reusableArtifactPaths: uniqueSortedArray(checkpointPathSchema, 1),
|
|
3102
|
+
finalizeCommand: z9.string().trim().min(1),
|
|
3103
|
+
credentialEpoch: z9.string().trim().min(1),
|
|
3104
|
+
requiredSecretNames: uniqueSortedArray(secretNameSchema).optional(),
|
|
3105
|
+
optionalSecretNames: uniqueSortedArray(secretNameSchema).optional(),
|
|
3106
|
+
bakeWebAppBuild: z9.boolean().optional()
|
|
3107
|
+
}).superRefine((checkpoint, ctx) => {
|
|
3108
|
+
const required = new Set(checkpoint.requiredSecretNames ?? []);
|
|
3109
|
+
for (const name of checkpoint.optionalSecretNames ?? []) {
|
|
3110
|
+
if (required.has(name)) {
|
|
3111
|
+
ctx.addIssue({
|
|
3112
|
+
code: z9.ZodIssueCode.custom,
|
|
3113
|
+
path: ["optionalSecretNames"],
|
|
3114
|
+
message: "A secret cannot be both required and optional"
|
|
3115
|
+
});
|
|
3116
|
+
}
|
|
3117
|
+
}
|
|
3118
|
+
});
|
|
3119
|
+
var ACHIEVEMENT_RARITIES = [
|
|
3120
|
+
{
|
|
3121
|
+
key: "common",
|
|
3122
|
+
name: "Common",
|
|
3123
|
+
color: "#22c55e",
|
|
3124
|
+
iconPath: "/storypoints/square-solid-full.svg"
|
|
3125
|
+
},
|
|
3126
|
+
{
|
|
3127
|
+
key: "magic",
|
|
3128
|
+
name: "Magic",
|
|
3129
|
+
color: "#3b82f6",
|
|
3130
|
+
iconPath: "/storypoints/diamond-solid-full.svg"
|
|
3131
|
+
},
|
|
3132
|
+
{ key: "rare", name: "Rare", color: "#eab308", iconPath: "/storypoints/gem-solid-full.svg" },
|
|
3133
|
+
{
|
|
3134
|
+
key: "unique",
|
|
3135
|
+
name: "Unique",
|
|
3136
|
+
color: "#f97316",
|
|
3137
|
+
iconPath: "/storypoints/scroll-sharp-solid-full.svg"
|
|
3138
|
+
},
|
|
3139
|
+
{ key: "pack", name: "Pack", color: "#9c27b0", iconPath: "/storypoints/pack.svg" }
|
|
2464
3140
|
];
|
|
2465
|
-
var
|
|
2466
|
-
|
|
2467
|
-
|
|
2468
|
-
|
|
2469
|
-
|
|
2470
|
-
|
|
3141
|
+
var RISK_LEVELS = ["critical", "high", "medium", "low"];
|
|
3142
|
+
var riskLevelSchema = z22.enum(RISK_LEVELS);
|
|
3143
|
+
var DEFAULT_RISK_LEVELS = [
|
|
3144
|
+
{
|
|
3145
|
+
level: "critical",
|
|
3146
|
+
value: 4,
|
|
3147
|
+
label: "Critical",
|
|
3148
|
+
description: "Touches critical surface area; give it the closest review.",
|
|
3149
|
+
color: "#dc2626",
|
|
3150
|
+
ordinal: 0
|
|
3151
|
+
},
|
|
3152
|
+
{
|
|
3153
|
+
level: "high",
|
|
3154
|
+
value: 3,
|
|
3155
|
+
label: "Elevated",
|
|
3156
|
+
description: "Touches important surface area; review carefully.",
|
|
3157
|
+
color: "#ea580c",
|
|
3158
|
+
ordinal: 1
|
|
3159
|
+
},
|
|
3160
|
+
{
|
|
3161
|
+
level: "medium",
|
|
3162
|
+
value: 2,
|
|
3163
|
+
label: "Moderate",
|
|
3164
|
+
description: "Moderate surface area; normal review.",
|
|
3165
|
+
color: "#d97706",
|
|
3166
|
+
ordinal: 2
|
|
3167
|
+
},
|
|
3168
|
+
{
|
|
3169
|
+
level: "low",
|
|
3170
|
+
value: 1,
|
|
3171
|
+
label: "Minimal",
|
|
3172
|
+
description: "Small or isolated surface area.",
|
|
3173
|
+
color: "#64748b",
|
|
3174
|
+
ordinal: 3
|
|
3175
|
+
}
|
|
3176
|
+
];
|
|
3177
|
+
var LEVEL_BY_VALUE = new Map(
|
|
3178
|
+
DEFAULT_RISK_LEVELS.map((m) => [m.value, m.level])
|
|
3179
|
+
);
|
|
3180
|
+
var ACTIVE_WORK_STATUSES = [
|
|
3181
|
+
"InProgress",
|
|
3182
|
+
"ReviewPR",
|
|
3183
|
+
"ReviewDev",
|
|
3184
|
+
"ReviewLive",
|
|
3185
|
+
"Complete"
|
|
3186
|
+
];
|
|
3187
|
+
var IDENTIFIED_WORK_STATUSES = ["Open", ...ACTIVE_WORK_STATUSES];
|
|
3188
|
+
var DEFAULT_TASK_STATUS_COLOR = "#a8a29e";
|
|
3189
|
+
var DEFAULT_TASK_STATUS_COLOR_INT = Number.parseInt(DEFAULT_TASK_STATUS_COLOR.slice(1), 16);
|
|
3190
|
+
var MAX_FILE_SIZE_BYTES2 = 25 * 1024 * 1024;
|
|
3191
|
+
var MAX_FILE_TAGS2 = 5;
|
|
3192
|
+
var MAX_FILE_TAG_LENGTH = 100;
|
|
3193
|
+
var EMBED_THRESHOLD_IMAGES = 5 * 1024 * 1024;
|
|
3194
|
+
var EMBED_THRESHOLD_TEXT = 2 * 1024 * 1024;
|
|
3195
|
+
var IDLE_HEARTBEAT_MS = 90 * 1e3;
|
|
3196
|
+
var TurnEndToolCallSchema = z32.object({
|
|
3197
|
+
tool: z32.string(),
|
|
3198
|
+
input: z32.string().optional(),
|
|
3199
|
+
output: z32.string().optional(),
|
|
3200
|
+
timestamp: z32.string().optional()
|
|
3201
|
+
}).passthrough();
|
|
3202
|
+
var KnownAgentEventSchema = z32.discriminatedUnion("type", [
|
|
3203
|
+
// ── Lifecycle / connection ────────────────────────────────────────────
|
|
3204
|
+
z32.object({
|
|
3205
|
+
type: z32.literal("connected"),
|
|
3206
|
+
sessionId: z32.string(),
|
|
3207
|
+
projectId: z32.string().optional()
|
|
3208
|
+
}).passthrough(),
|
|
3209
|
+
// Open-ended context snapshot spread from buildInitializationContext().
|
|
3210
|
+
z32.object({ type: z32.literal("session_manifest") }).passthrough(),
|
|
3211
|
+
z32.object({
|
|
3212
|
+
type: z32.literal("agent_runner_status"),
|
|
3213
|
+
reason: z32.string(),
|
|
3214
|
+
attempt: z32.number().optional(),
|
|
3215
|
+
attempts: z32.number().optional()
|
|
3216
|
+
}).passthrough(),
|
|
3217
|
+
z32.object({ type: z32.literal("shutdown"), reason: z32.string().optional() }).passthrough(),
|
|
3218
|
+
z32.object({ type: z32.literal("mode_changed"), agentMode: z32.string() }).passthrough(),
|
|
3219
|
+
z32.object({ type: z32.literal("mode_transition"), from: z32.string(), to: z32.string() }).passthrough(),
|
|
3220
|
+
// ── Turn stream ───────────────────────────────────────────────────────
|
|
3221
|
+
z32.object({ type: z32.literal("message"), content: z32.string() }).passthrough(),
|
|
3222
|
+
z32.object({ type: z32.literal("thinking"), message: z32.string() }).passthrough(),
|
|
3223
|
+
z32.object({
|
|
3224
|
+
type: z32.literal("tool_use"),
|
|
3225
|
+
tool: z32.string(),
|
|
3226
|
+
// Producers send JSON.stringify(input); consumers defend against
|
|
3227
|
+
// object inputs from older agents, so the wire stays permissive here.
|
|
3228
|
+
input: z32.unknown().optional()
|
|
3229
|
+
}).passthrough(),
|
|
3230
|
+
z32.object({
|
|
3231
|
+
type: z32.literal("tool_result"),
|
|
3232
|
+
tool: z32.string(),
|
|
3233
|
+
output: z32.unknown().optional(),
|
|
3234
|
+
isError: z32.boolean().optional(),
|
|
3235
|
+
redactedCount: z32.number().optional()
|
|
3236
|
+
}).passthrough(),
|
|
3237
|
+
z32.object({ type: z32.literal("turn_end"), toolCalls: z32.array(TurnEndToolCallSchema) }).passthrough(),
|
|
3238
|
+
z32.object({
|
|
3239
|
+
type: z32.literal("completed"),
|
|
3240
|
+
summary: z32.string().optional(),
|
|
3241
|
+
durationMs: z32.number().optional()
|
|
3242
|
+
}).passthrough(),
|
|
3243
|
+
z32.object({ type: z32.literal("error"), message: z32.string() }).passthrough(),
|
|
3244
|
+
z32.object({ type: z32.literal("agent_typing_start") }).passthrough(),
|
|
3245
|
+
z32.object({ type: z32.literal("agent_typing_stop") }).passthrough(),
|
|
3246
|
+
// ── Telemetry ─────────────────────────────────────────────────────────
|
|
3247
|
+
// heartbeat/typing: legacy telemetry the server still classifies as
|
|
3248
|
+
// transient (TRANSIENT_EVENT_TYPES) — kept in the vocabulary.
|
|
3249
|
+
z32.object({ type: z32.literal("heartbeat") }).passthrough(),
|
|
3250
|
+
z32.object({ type: z32.literal("typing") }).passthrough(),
|
|
3251
|
+
z32.object({
|
|
3252
|
+
type: z32.literal("context_update"),
|
|
3253
|
+
contextTokens: z32.number(),
|
|
3254
|
+
contextWindow: z32.number(),
|
|
3255
|
+
inputTokens: z32.number().optional(),
|
|
3256
|
+
cacheReadInputTokens: z32.number().optional(),
|
|
3257
|
+
cacheCreationInputTokens: z32.number().optional(),
|
|
3258
|
+
totalTokensUsed: z32.number().optional()
|
|
3259
|
+
}).passthrough(),
|
|
3260
|
+
// Four producer shapes share this type: {rateLimitType, utilization, status}
|
|
3261
|
+
// (SDK rate_limit_event), {resetsAt} (agent-connection resume notice), the
|
|
3262
|
+
// usage-sampler ({rateLimitType, utilization, status, resetsAt, gauges}
|
|
3263
|
+
// — resetsAt matches rateLimitType; gauges survives via .passthrough()), and
|
|
3264
|
+
// {unmeasurable, reason} (the sampler reporting it cannot read this key).
|
|
3265
|
+
z32.object({
|
|
3266
|
+
type: z32.literal("rate_limit_update"),
|
|
3267
|
+
rateLimitType: z32.string().optional(),
|
|
3268
|
+
utilization: z32.number().optional(),
|
|
3269
|
+
status: z32.string().optional(),
|
|
3270
|
+
resetsAt: z32.string().optional(),
|
|
3271
|
+
unmeasurable: z32.boolean().optional(),
|
|
3272
|
+
reason: z32.string().optional()
|
|
3273
|
+
}).passthrough(),
|
|
3274
|
+
z32.object({
|
|
3275
|
+
type: z32.literal("context_compacted"),
|
|
3276
|
+
trigger: z32.string().optional(),
|
|
3277
|
+
preTokens: z32.number().optional()
|
|
3278
|
+
}).passthrough(),
|
|
3279
|
+
z32.object({
|
|
3280
|
+
type: z32.literal("tool_progress"),
|
|
3281
|
+
toolName: z32.string().optional(),
|
|
3282
|
+
elapsedSeconds: z32.number().optional()
|
|
3283
|
+
}).passthrough(),
|
|
3284
|
+
z32.object({
|
|
3285
|
+
type: z32.literal("subagent_started"),
|
|
3286
|
+
sdkTaskId: z32.string().optional(),
|
|
3287
|
+
description: z32.string().optional()
|
|
3288
|
+
}).passthrough(),
|
|
3289
|
+
z32.object({
|
|
3290
|
+
type: z32.literal("subagent_progress"),
|
|
3291
|
+
sdkTaskId: z32.string().optional(),
|
|
3292
|
+
description: z32.string().optional(),
|
|
3293
|
+
toolUses: z32.number().optional(),
|
|
3294
|
+
durationMs: z32.number().optional()
|
|
3295
|
+
}).passthrough(),
|
|
3296
|
+
// ── Work products ─────────────────────────────────────────────────────
|
|
3297
|
+
z32.object({ type: z32.literal("pr_created"), url: z32.string(), number: z32.number() }).passthrough(),
|
|
3298
|
+
z32.object({
|
|
3299
|
+
type: z32.literal("code_review_complete"),
|
|
3300
|
+
result: z32.enum(["approved", "changes_requested"]),
|
|
3301
|
+
summary: z32.string().optional(),
|
|
3302
|
+
issues: z32.array(
|
|
3303
|
+
z32.object({
|
|
3304
|
+
file: z32.string(),
|
|
3305
|
+
line: z32.number().optional(),
|
|
3306
|
+
severity: z32.string().optional(),
|
|
3307
|
+
description: z32.string().optional()
|
|
3308
|
+
}).passthrough()
|
|
3309
|
+
).optional()
|
|
3310
|
+
}).passthrough(),
|
|
3311
|
+
// ── Environment setup / start command ─────────────────────────────────
|
|
3312
|
+
z32.object({ type: z32.literal("setup_output"), stream: z32.string(), data: z32.string() }).passthrough(),
|
|
3313
|
+
z32.object({
|
|
3314
|
+
type: z32.literal("setup_complete"),
|
|
3315
|
+
startCommandRunning: z32.boolean().optional(),
|
|
3316
|
+
startCommandConfigured: z32.boolean().optional(),
|
|
3317
|
+
// Sanitized server-side by sanitizeSessionPreviewPorts — stays unknown.
|
|
3318
|
+
previewPorts: z32.unknown().optional()
|
|
3319
|
+
}).passthrough(),
|
|
3320
|
+
z32.object({ type: z32.literal("setup_error"), message: z32.string() }).passthrough(),
|
|
3321
|
+
z32.object({ type: z32.literal("start_command_started") }).passthrough(),
|
|
3322
|
+
z32.object({ type: z32.literal("start_command_output"), stream: z32.string(), data: z32.string() }).passthrough(),
|
|
3323
|
+
z32.object({
|
|
3324
|
+
type: z32.literal("start_command_exited"),
|
|
3325
|
+
code: z32.number().nullable().optional(),
|
|
3326
|
+
signal: z32.string().nullable().optional(),
|
|
3327
|
+
message: z32.string().optional()
|
|
3328
|
+
}).passthrough(),
|
|
3329
|
+
z32.object({ type: z32.literal("start_command_error"), message: z32.string() }).passthrough()
|
|
3330
|
+
]);
|
|
3331
|
+
var AgentEventSchema = z32.union([
|
|
3332
|
+
KnownAgentEventSchema,
|
|
3333
|
+
z32.object({ type: z32.string().min(1) }).catchall(z32.unknown())
|
|
3334
|
+
]);
|
|
3335
|
+
var cardDescription = z42.string().max(CARD_DESCRIPTION_MAX, CARD_DESCRIPTION_LIMIT_MESSAGE).optional();
|
|
3336
|
+
var AgentHeartbeatSchema = z42.object({
|
|
3337
|
+
sessionId: z42.string().optional(),
|
|
3338
|
+
timestamp: z42.string(),
|
|
3339
|
+
status: z42.enum(["active", "idle", "building"]),
|
|
3340
|
+
currentAction: z42.string().optional(),
|
|
3341
|
+
/** Sender-observed main event-loop lag (ms) — see AgentHeartbeat.loopLagMs. */
|
|
3342
|
+
loopLagMs: z42.number().nonnegative().optional()
|
|
3343
|
+
});
|
|
3344
|
+
var CreatePRInputSchema = z42.object({
|
|
3345
|
+
title: z42.string().min(1),
|
|
3346
|
+
body: z42.string(),
|
|
3347
|
+
head: z42.string().optional(),
|
|
3348
|
+
base: z42.string().optional()
|
|
3349
|
+
});
|
|
3350
|
+
var PostToChatInputSchema = z42.object({
|
|
3351
|
+
message: z42.string().min(1),
|
|
3352
|
+
type: z42.enum(["message", "question", "update"]).optional().default("message"),
|
|
3353
|
+
milestone: z42.enum(["plan_ready", "implementation_complete", "blocked"]).optional()
|
|
3354
|
+
});
|
|
3355
|
+
var GetTaskContextRequestSchema = z42.object({
|
|
3356
|
+
sessionId: z42.string(),
|
|
3357
|
+
includeHistory: z42.boolean().optional().default(false),
|
|
3358
|
+
/**
|
|
3359
|
+
* Read the plan-revised marker WITHOUT consuming it. Bookkeeping fetches
|
|
3360
|
+
* (the session-identity check, the branch refresh) pass true so they cannot
|
|
3361
|
+
* race the boot fetch and swallow the notice before it reaches the prompt.
|
|
3362
|
+
* Defaults to false — consuming — so a pod running an older agent build still
|
|
3363
|
+
* clears the marker instead of showing the notice on every boot forever.
|
|
3364
|
+
*/
|
|
3365
|
+
peekPlanRevision: z42.boolean().optional().default(false)
|
|
3366
|
+
});
|
|
3367
|
+
var GetChatMessagesRequestSchema = z42.object({
|
|
3368
|
+
sessionId: z42.string(),
|
|
3369
|
+
limit: z42.number().int().positive().optional().default(50),
|
|
3370
|
+
offset: z42.number().int().nonnegative().optional().default(0),
|
|
3371
|
+
/** Task id or slug to read chat from. Omit for the session's own task. Only
|
|
3372
|
+
* the session's own task or one of its children resolves — anything else is
|
|
3373
|
+
* an error, never a silent fallback to the caller's own chat. */
|
|
3374
|
+
taskId: z42.string().optional()
|
|
3375
|
+
});
|
|
3376
|
+
var GetTaskFilesRequestSchema = z42.object({
|
|
3377
|
+
sessionId: z42.string()
|
|
3378
|
+
});
|
|
3379
|
+
var GetTaskFileRequestSchema = z42.object({
|
|
3380
|
+
sessionId: z42.string(),
|
|
3381
|
+
fileId: z42.string()
|
|
3382
|
+
});
|
|
3383
|
+
var GetTaskRequestSchema = z42.object({
|
|
3384
|
+
sessionId: z42.string(),
|
|
3385
|
+
taskSlugOrId: z42.string()
|
|
3386
|
+
});
|
|
3387
|
+
var GetCliHistoryRequestSchema = z42.object({
|
|
3388
|
+
sessionId: z42.string(),
|
|
3389
|
+
limit: z42.number().int().positive().optional().default(100),
|
|
3390
|
+
source: z42.enum(["agent", "application"]).optional(),
|
|
3391
|
+
/** Task id or slug to read logs from. Omit for the session's own task. Only
|
|
3392
|
+
* the session's own task or one of its children resolves — anything else is
|
|
3393
|
+
* an error, never a silent fallback to the caller's own logs. */
|
|
3394
|
+
taskId: z42.string().optional()
|
|
3395
|
+
});
|
|
3396
|
+
var ListSubtasksRequestSchema = z42.object({
|
|
3397
|
+
sessionId: z42.string(),
|
|
3398
|
+
/** "compact" returns the slim orchestration view (ListSubtasksCompactResponse)
|
|
3399
|
+
* with the pack build-slot picture; "full" (default — wire-compat with older
|
|
3400
|
+
* agents) returns the verbose SubtaskSummaryDTO[] including description/plan. */
|
|
3401
|
+
view: z42.enum(["compact", "full"]).optional()
|
|
3402
|
+
});
|
|
3403
|
+
var GetDependenciesRequestSchema = z42.object({
|
|
3404
|
+
sessionId: z42.string()
|
|
3405
|
+
});
|
|
3406
|
+
var GetSuggestionsRequestSchema = z42.object({
|
|
3407
|
+
sessionId: z42.string(),
|
|
3408
|
+
status: z42.string().optional(),
|
|
3409
|
+
limit: z42.number().int().min(1).max(100).optional()
|
|
3410
|
+
});
|
|
3411
|
+
var ListManualTestsRequestSchema = z42.object({
|
|
3412
|
+
sessionId: z42.string()
|
|
3413
|
+
});
|
|
3414
|
+
var QueryManualTestsRequestSchema = z42.object({
|
|
3415
|
+
sessionId: z42.string(),
|
|
3416
|
+
cardStatuses: z42.array(z42.string()).optional(),
|
|
3417
|
+
testStatuses: z42.array(z42.enum(["open", "approved", "rejected"])).optional()
|
|
3418
|
+
});
|
|
3419
|
+
var CreatePullRequestRequestSchema = CreatePRInputSchema.extend({ sessionId: z42.string() });
|
|
3420
|
+
var RequestFileUploadRequestSchema = z42.object({
|
|
3421
|
+
sessionId: z42.string(),
|
|
3422
|
+
fileName: z42.string().min(1).max(255),
|
|
3423
|
+
mimeType: z42.string().min(1).max(128),
|
|
3424
|
+
fileSize: z42.number().int().positive().max(MAX_FILE_SIZE_BYTES2)
|
|
3425
|
+
});
|
|
3426
|
+
var ConfirmFileUploadRequestSchema = z42.object({
|
|
3427
|
+
sessionId: z42.string(),
|
|
3428
|
+
fileId: z42.string(),
|
|
3429
|
+
title: z42.string().max(500).optional(),
|
|
3430
|
+
/** Glossary tag names (or ids) this file is an example of. */
|
|
3431
|
+
tags: z42.array(z42.string().min(1).max(MAX_FILE_TAG_LENGTH)).max(MAX_FILE_TAGS2).optional()
|
|
3432
|
+
});
|
|
3433
|
+
var UpdateTaskStatusRequestSchema = z42.object({
|
|
3434
|
+
sessionId: z42.string(),
|
|
3435
|
+
status: z42.string(),
|
|
3436
|
+
force: z42.boolean().optional().default(false)
|
|
3437
|
+
});
|
|
3438
|
+
var StoreSessionIdRequestSchema = z42.object({
|
|
3439
|
+
sessionId: z42.string(),
|
|
3440
|
+
sdkSessionId: z42.string()
|
|
3441
|
+
});
|
|
3442
|
+
var SetManualTestsRequestSchema = z42.object({
|
|
3443
|
+
sessionId: z42.string(),
|
|
3444
|
+
items: z42.array(z42.object({ title: z42.string().min(1) })).min(1)
|
|
3445
|
+
});
|
|
3446
|
+
var EditManualTestRequestSchema = z42.object({
|
|
3447
|
+
sessionId: z42.string(),
|
|
3448
|
+
title: z42.string().min(1),
|
|
3449
|
+
newTitle: z42.string().min(1)
|
|
3450
|
+
});
|
|
3451
|
+
var RemoveManualTestRequestSchema = z42.object({
|
|
3452
|
+
sessionId: z42.string(),
|
|
3453
|
+
title: z42.string().min(1)
|
|
3454
|
+
});
|
|
3455
|
+
var ApproveManualTestRequestSchema = z42.object({
|
|
3456
|
+
sessionId: z42.string(),
|
|
3457
|
+
title: z42.string().min(1)
|
|
3458
|
+
});
|
|
3459
|
+
var RejectManualTestRequestSchema = z42.object({
|
|
3460
|
+
sessionId: z42.string(),
|
|
3461
|
+
title: z42.string().min(1),
|
|
3462
|
+
reason: z42.string().min(1).max(2e3)
|
|
3463
|
+
});
|
|
3464
|
+
var SessionStartRequestSchema = z42.object({
|
|
3465
|
+
sessionId: z42.string(),
|
|
3466
|
+
agentVersion: z42.string(),
|
|
3467
|
+
capabilities: z42.array(z42.string())
|
|
3468
|
+
});
|
|
3469
|
+
var SessionStopRequestSchema = z42.object({
|
|
3470
|
+
sessionId: z42.string(),
|
|
3471
|
+
reason: z42.string().optional()
|
|
3472
|
+
});
|
|
3473
|
+
var EndReviewSessionRequestSchema = z42.object({
|
|
3474
|
+
sessionId: z42.string(),
|
|
3475
|
+
reason: z42.enum(["approved", "changes_requested", "finished"]).optional()
|
|
3476
|
+
});
|
|
3477
|
+
var ConnectAgentRequestSchema = z42.object({
|
|
3478
|
+
sessionId: z42.string()
|
|
3479
|
+
});
|
|
3480
|
+
var ReportAgentStatusRequestSchema = z42.object({
|
|
3481
|
+
sessionId: z42.string(),
|
|
3482
|
+
status: z42.string(),
|
|
3483
|
+
/** Why the agent reports this status (e.g. "user_question" while an AskUserQuestion questionnaire is pending in the TUI). */
|
|
3484
|
+
reason: z42.string().optional(),
|
|
3485
|
+
/**
|
|
3486
|
+
* The pending question text, sent only alongside `reason: "user_question"`
|
|
3487
|
+
* so the server can surface it in the user-question notification body (and
|
|
3488
|
+
* thus the Attention feed) instead of a generic string. Optional: older
|
|
3489
|
+
* agents omit it and the server falls back to the generic wording.
|
|
3490
|
+
*/
|
|
3491
|
+
questionText: z42.string().optional()
|
|
3492
|
+
});
|
|
3493
|
+
var NotifyAgentVersionRequestSchema = z42.object({
|
|
3494
|
+
sessionId: z42.string(),
|
|
3495
|
+
agentVersion: z42.string()
|
|
3496
|
+
});
|
|
3497
|
+
var DiscoveredPortSchema = z42.object({
|
|
3498
|
+
port: z42.number().int().min(1).max(65535),
|
|
3499
|
+
label: z42.string().min(1).max(64).optional(),
|
|
3500
|
+
protocol: z42.enum(["http", "tcp"]).optional(),
|
|
3501
|
+
detectedAt: z42.string()
|
|
3502
|
+
});
|
|
3503
|
+
var ReportDiscoveredPortsRequestSchema = z42.object({
|
|
3504
|
+
sessionId: z42.string(),
|
|
3505
|
+
ports: z42.array(DiscoveredPortSchema).max(64)
|
|
3506
|
+
});
|
|
3507
|
+
var ReportBootMilestoneRequestSchema = z42.object({
|
|
3508
|
+
sessionId: z42.string(),
|
|
3509
|
+
key: z42.string().max(64)
|
|
3510
|
+
});
|
|
3511
|
+
var CreateSubtaskRequestSchema = z42.object({
|
|
3512
|
+
sessionId: z42.string(),
|
|
3513
|
+
title: z42.string().min(1),
|
|
3514
|
+
description: cardDescription,
|
|
3515
|
+
plan: z42.string().optional(),
|
|
3516
|
+
storyPointValue: z42.number().int().positive().optional(),
|
|
3517
|
+
ordinal: z42.number().int().nonnegative().optional(),
|
|
3518
|
+
followParentStatus: z42.boolean().optional(),
|
|
3519
|
+
/** Sibling subtask ids or slugs this subtask blocks on (explicit dependency
|
|
3520
|
+
* metadata — preferred over encoding order in plan text / ordinal). */
|
|
3521
|
+
dependsOn: z42.array(z42.string().min(1)).max(32).optional(),
|
|
3522
|
+
/** Glossary tag names to assign to the child. Unmatched names come back in
|
|
3523
|
+
* the response rather than failing the create. */
|
|
3524
|
+
tags: z42.array(z42.string().min(1)).max(10).optional()
|
|
3525
|
+
});
|
|
3526
|
+
var UpdateSubtaskRequestSchema = z42.object({
|
|
3527
|
+
sessionId: z42.string(),
|
|
3528
|
+
subtaskId: z42.string(),
|
|
3529
|
+
title: z42.string().min(1).optional(),
|
|
3530
|
+
description: cardDescription,
|
|
3531
|
+
plan: z42.string().optional(),
|
|
3532
|
+
/** Orchestration statuses only ("Planning" | "Open") — the pack parent's
|
|
3533
|
+
* sanctioned promotion path. Execution statuses stay with the build
|
|
3534
|
+
* pipeline / force_update_task_status. Enforced server-side. */
|
|
3535
|
+
status: z42.string().optional(),
|
|
3536
|
+
/** Assign a project agent to the child — accepts the agent's id or exact
|
|
3537
|
+
* name; resolved against the parent task's project server-side. */
|
|
3538
|
+
agentIdOrName: z42.string().min(1).optional(),
|
|
3539
|
+
storyPointValue: z42.number().int().positive().optional(),
|
|
3540
|
+
followParentStatus: z42.boolean().optional(),
|
|
3541
|
+
/** Replace this subtask's dependency edges with these sibling ids/slugs.
|
|
3542
|
+
* Empty array clears all. Omit to leave dependencies unchanged. */
|
|
3543
|
+
dependsOn: z42.array(z42.string().min(1)).max(32).optional()
|
|
3544
|
+
});
|
|
3545
|
+
var DeleteSubtaskRequestSchema = z42.object({
|
|
3546
|
+
sessionId: z42.string(),
|
|
3547
|
+
subtaskId: z42.string()
|
|
3548
|
+
});
|
|
3549
|
+
var SetSubtaskParentRequestSchema = z42.object({
|
|
3550
|
+
sessionId: z42.string(),
|
|
3551
|
+
taskId: z42.string().min(1),
|
|
3552
|
+
detach: z42.boolean().optional(),
|
|
3553
|
+
ordinal: z42.number().int().nonnegative().optional(),
|
|
3554
|
+
followParentStatus: z42.boolean().optional()
|
|
3555
|
+
});
|
|
3556
|
+
var GetTaskPropertiesRequestSchema = z42.object({
|
|
3557
|
+
sessionId: z42.string()
|
|
3558
|
+
});
|
|
3559
|
+
var UpdateTaskFieldsRequestSchema = z42.object({
|
|
3560
|
+
sessionId: z42.string(),
|
|
3561
|
+
plan: z42.string().optional(),
|
|
3562
|
+
description: cardDescription
|
|
3563
|
+
});
|
|
3564
|
+
var UpdateTaskPropertiesRequestSchema = z42.object({
|
|
3565
|
+
sessionId: z42.string(),
|
|
3566
|
+
title: z42.string().optional(),
|
|
3567
|
+
storyPointValue: z42.number().int().positive().optional(),
|
|
3568
|
+
tagIds: z42.array(z42.string()).optional(),
|
|
3569
|
+
tagNames: z42.array(z42.string()).optional(),
|
|
3570
|
+
githubPRUrl: z42.string().url().optional(),
|
|
3571
|
+
githubBranch: z42.string().optional(),
|
|
3572
|
+
// Canonical risk level, or null to clear — same semantics as the headless
|
|
3573
|
+
// update_task boundary (resolved to the project's Risk row in the handler).
|
|
3574
|
+
risk: riskLevelSchema.nullable().optional()
|
|
3575
|
+
});
|
|
3576
|
+
var ListIconsRequestSchema = z42.object({
|
|
3577
|
+
sessionId: z42.string()
|
|
3578
|
+
});
|
|
3579
|
+
var GenerateTaskIconRequestSchema = z42.object({
|
|
3580
|
+
sessionId: z42.string(),
|
|
3581
|
+
prompt: z42.string().min(1),
|
|
3582
|
+
aspectRatio: z42.string().optional()
|
|
3583
|
+
});
|
|
3584
|
+
var SearchFaIconsRequestSchema = z42.object({
|
|
3585
|
+
sessionId: z42.string(),
|
|
3586
|
+
query: z42.string().min(1),
|
|
3587
|
+
first: z42.number().int().positive().optional()
|
|
3588
|
+
});
|
|
3589
|
+
var PickFaIconRequestSchema = z42.object({
|
|
3590
|
+
sessionId: z42.string(),
|
|
3591
|
+
fontAwesomeId: z42.string().min(1),
|
|
3592
|
+
fontAwesomeStyle: z42.string().optional()
|
|
3593
|
+
});
|
|
3594
|
+
var CreateFollowUpTaskRequestSchema = z42.object({
|
|
3595
|
+
sessionId: z42.string(),
|
|
3596
|
+
title: z42.string().min(1),
|
|
3597
|
+
description: cardDescription,
|
|
3598
|
+
plan: z42.string().optional(),
|
|
3599
|
+
storyPointValue: z42.number().int().positive().optional()
|
|
3600
|
+
});
|
|
3601
|
+
var AddDependencyRequestSchema = z42.object({
|
|
3602
|
+
sessionId: z42.string(),
|
|
3603
|
+
dependsOnSlugOrId: z42.string()
|
|
3604
|
+
});
|
|
3605
|
+
var RemoveDependencyRequestSchema = z42.object({
|
|
3606
|
+
sessionId: z42.string(),
|
|
3607
|
+
dependsOnSlugOrId: z42.string()
|
|
3608
|
+
});
|
|
3609
|
+
var CreateSuggestionRequestSchema = z42.object({
|
|
3610
|
+
sessionId: z42.string(),
|
|
3611
|
+
title: z42.string().min(1),
|
|
3612
|
+
description: cardDescription,
|
|
3613
|
+
tagNames: z42.array(z42.string()).optional()
|
|
3614
|
+
});
|
|
3615
|
+
var VoteSuggestionRequestSchema = z42.object({
|
|
3616
|
+
sessionId: z42.string(),
|
|
3617
|
+
suggestionId: z42.string(),
|
|
3618
|
+
value: z42.union([z42.literal(1), z42.literal(-1)])
|
|
3619
|
+
});
|
|
3620
|
+
var TriggerIdentificationRequestSchema = z42.object({
|
|
3621
|
+
sessionId: z42.string()
|
|
3622
|
+
});
|
|
3623
|
+
var HandoffToImplementerRequestSchema = z42.object({
|
|
3624
|
+
sessionId: z42.string(),
|
|
3625
|
+
// Optional difficulty sizing — sets the task's story points before resolving
|
|
3626
|
+
// the matched implementer agent. Omit to hand off using the task's current
|
|
3627
|
+
// story points (or the project's default task agent when unsized).
|
|
3628
|
+
storyPoints: z42.number().int().positive().optional(),
|
|
3629
|
+
// Optional kickoff note posted to the task chat alongside the handoff notice.
|
|
3630
|
+
message: z42.string().optional()
|
|
3631
|
+
});
|
|
3632
|
+
var SubmitCodeReviewResultRequestSchema = z42.object({
|
|
3633
|
+
sessionId: z42.string(),
|
|
3634
|
+
approved: z42.boolean(),
|
|
3635
|
+
content: z42.string(),
|
|
3636
|
+
// Canonical risk level the reviewer assigned to this change. Required on every
|
|
3637
|
+
// verdict — the reviewer must judge it. Applied authoritatively server-side
|
|
3638
|
+
// (may raise OR lower an already-set value; the reviewer has that authority).
|
|
3639
|
+
risk: riskLevelSchema,
|
|
3640
|
+
// The commit SHA the reviewer actually reviewed. When present, the verdict is
|
|
3641
|
+
// rejected unless the task is still at this SHA (guards against a late
|
|
3642
|
+
// old-SHA verdict overwriting a newer review cycle).
|
|
3643
|
+
reviewedSha: z42.string().optional()
|
|
3644
|
+
});
|
|
3645
|
+
var CycleCodingAgentKeyRequestSchema = z42.object({
|
|
3646
|
+
sessionId: z42.string(),
|
|
3647
|
+
rateLimitType: z42.string(),
|
|
3648
|
+
resetsAt: z42.string().optional()
|
|
3649
|
+
});
|
|
3650
|
+
var StartChildCloudBuildRequestSchema = z42.object({
|
|
3651
|
+
sessionId: z42.string(),
|
|
3652
|
+
childTaskId: z42.string()
|
|
3653
|
+
});
|
|
3654
|
+
var StopChildBuildRequestSchema = z42.object({
|
|
3655
|
+
sessionId: z42.string(),
|
|
3656
|
+
childTaskId: z42.string()
|
|
3657
|
+
});
|
|
3658
|
+
var ApproveAndMergePRRequestSchema = z42.object({
|
|
3659
|
+
sessionId: z42.string(),
|
|
3660
|
+
childTaskId: z42.string()
|
|
3661
|
+
});
|
|
3662
|
+
var PostChildChatMessageRequestSchema = z42.object({
|
|
3663
|
+
sessionId: z42.string(),
|
|
3664
|
+
childTaskId: z42.string(),
|
|
3665
|
+
message: z42.string().min(1)
|
|
3666
|
+
});
|
|
3667
|
+
var UpdateChildStatusRequestSchema = z42.object({
|
|
3668
|
+
sessionId: z42.string(),
|
|
3669
|
+
childTaskId: z42.string(),
|
|
3670
|
+
status: z42.string()
|
|
3671
|
+
});
|
|
3672
|
+
var GetAgentStatusRequestSchema = z42.object({
|
|
3673
|
+
taskId: z42.string()
|
|
3674
|
+
});
|
|
3675
|
+
var GetUiCliHistoryRequestSchema = z42.object({
|
|
3676
|
+
taskId: z42.string()
|
|
3677
|
+
});
|
|
3678
|
+
var GetActivePtySessionRequestSchema = z42.object({
|
|
3679
|
+
taskId: z42.string()
|
|
3680
|
+
});
|
|
3681
|
+
var ListActivePtySessionsRequestSchema = z42.object({
|
|
3682
|
+
taskId: z42.string()
|
|
3683
|
+
});
|
|
3684
|
+
var SendSoftStopRequestSchema = z42.object({
|
|
3685
|
+
taskId: z42.string()
|
|
3686
|
+
});
|
|
3687
|
+
var StopTaskSessionRequestSchema = z42.object({
|
|
3688
|
+
taskId: z42.string(),
|
|
3689
|
+
sessionId: z42.string()
|
|
3690
|
+
});
|
|
3691
|
+
var FlushTaskQueueRequestSchema = z42.object({
|
|
3692
|
+
taskId: z42.string(),
|
|
3693
|
+
softStop: z42.boolean().optional()
|
|
3694
|
+
});
|
|
3695
|
+
var CancelTaskQueuedMessageRequestSchema = z42.object({
|
|
3696
|
+
taskId: z42.string(),
|
|
3697
|
+
messageId: z42.string()
|
|
3698
|
+
});
|
|
3699
|
+
var FlushSingleQueuedMessageRequestSchema = z42.object({
|
|
3700
|
+
taskId: z42.string(),
|
|
3701
|
+
messageId: z42.string(),
|
|
3702
|
+
softStop: z42.boolean().optional()
|
|
3703
|
+
});
|
|
3704
|
+
var AnswerAgentQuestionRequestSchema = z42.object({
|
|
3705
|
+
taskId: z42.string(),
|
|
3706
|
+
requestId: z42.string(),
|
|
3707
|
+
answers: z42.record(z42.string(), z42.string())
|
|
3708
|
+
});
|
|
3709
|
+
var ClearAgentTodosRequestSchema = z42.object({
|
|
3710
|
+
taskId: z42.string()
|
|
3711
|
+
});
|
|
3712
|
+
var AgentQuestionOptionSchema = z42.object({
|
|
3713
|
+
label: z42.string(),
|
|
3714
|
+
description: z42.string(),
|
|
3715
|
+
preview: z42.string().optional()
|
|
3716
|
+
});
|
|
3717
|
+
var AgentQuestionSchema = z42.object({
|
|
3718
|
+
question: z42.string(),
|
|
3719
|
+
header: z42.string(),
|
|
3720
|
+
options: z42.array(AgentQuestionOptionSchema),
|
|
3721
|
+
multiSelect: z42.boolean().optional()
|
|
3722
|
+
});
|
|
3723
|
+
var AskUserQuestionRequestSchema = z42.object({
|
|
3724
|
+
sessionId: z42.string(),
|
|
3725
|
+
question: z42.string().min(1),
|
|
3726
|
+
requestId: z42.string().min(1),
|
|
3727
|
+
questions: z42.array(AgentQuestionSchema).min(1)
|
|
3728
|
+
});
|
|
3729
|
+
var PostAgentMessageRequestSchema = z42.object({
|
|
3730
|
+
sessionId: z42.string().min(1),
|
|
3731
|
+
content: z42.string(),
|
|
3732
|
+
milestone: z42.enum(["plan_ready", "implementation_complete", "blocked"]).optional()
|
|
3733
|
+
});
|
|
3734
|
+
var EmitAgentEventRequestSchema = z42.object({
|
|
3735
|
+
sessionId: z42.string(),
|
|
3736
|
+
events: z42.array(AgentEventSchema).max(500)
|
|
3737
|
+
});
|
|
3738
|
+
var RefreshGithubTokenRequestSchema = z42.object({
|
|
3739
|
+
sessionId: z42.string(),
|
|
3740
|
+
forceFresh: z42.boolean().optional()
|
|
3741
|
+
});
|
|
3742
|
+
var ReportCredentialFailureRequestSchema = z42.object({
|
|
3743
|
+
sessionId: z42.string(),
|
|
3744
|
+
error: z42.string().max(2e3).optional(),
|
|
3745
|
+
tokenShape: z42.string().max(500).optional(),
|
|
3746
|
+
healed: z42.boolean().optional()
|
|
3747
|
+
});
|
|
3748
|
+
var ReportReviewSpawnFailureRequestSchema = z42.object({
|
|
3749
|
+
sessionId: z42.string(),
|
|
3750
|
+
reviewSessionId: z42.string(),
|
|
3751
|
+
error: z42.string().max(2e3).optional()
|
|
3752
|
+
});
|
|
3753
|
+
var ReportBuilderSpawnFailureRequestSchema = ReportReviewSpawnFailureRequestSchema.omit({
|
|
3754
|
+
reviewSessionId: true
|
|
3755
|
+
}).extend({ buildSessionId: z42.string() });
|
|
3756
|
+
var RequestWorkspaceRecycleRequestSchema = z42.object({
|
|
3757
|
+
sessionId: z42.string(),
|
|
3758
|
+
reason: z42.string().max(2e3)
|
|
3759
|
+
});
|
|
3760
|
+
var SpawnTaskSessionRequestSchema = z42.object({
|
|
3761
|
+
taskId: z42.string(),
|
|
3762
|
+
kind: z42.enum(["tui", "shell"])
|
|
3763
|
+
});
|
|
3764
|
+
var StartCodeReviewRequestSchema = z42.object({
|
|
3765
|
+
taskId: z42.string(),
|
|
3766
|
+
force: z42.boolean().optional()
|
|
3767
|
+
});
|
|
3768
|
+
var StopCodeReviewRequestSchema = z42.object({
|
|
3769
|
+
taskId: z42.string()
|
|
3770
|
+
});
|
|
3771
|
+
var ReportSessionSpawnFailureRequestSchema = z42.object({
|
|
3772
|
+
sessionId: z42.string(),
|
|
3773
|
+
spawnedSessionId: z42.string(),
|
|
3774
|
+
error: z42.string().max(2e3).optional()
|
|
3775
|
+
});
|
|
3776
|
+
var RefreshGithubTokenResponseSchema = z42.object({
|
|
3777
|
+
token: z42.string()
|
|
3778
|
+
});
|
|
3779
|
+
var PTY_FRAME_MAX_CHARS = 256 * 1024;
|
|
3780
|
+
var PTY_MAX_DIMENSION = 1e3;
|
|
3781
|
+
var PtyOutputRequestSchema = z42.object({
|
|
3782
|
+
sessionId: z42.string(),
|
|
3783
|
+
data: z42.string().max(PTY_FRAME_MAX_CHARS),
|
|
3784
|
+
cols: z42.number().int().positive().max(PTY_MAX_DIMENSION).optional(),
|
|
3785
|
+
rows: z42.number().int().positive().max(PTY_MAX_DIMENSION).optional()
|
|
3786
|
+
});
|
|
3787
|
+
var PtyEndedRequestSchema = z42.object({
|
|
3788
|
+
sessionId: z42.string()
|
|
3789
|
+
});
|
|
3790
|
+
var PtyInputRequestSchema = z42.object({
|
|
3791
|
+
sessionId: z42.string(),
|
|
3792
|
+
data: z42.string().max(PTY_FRAME_MAX_CHARS)
|
|
3793
|
+
});
|
|
3794
|
+
var PtyResizeRequestSchema = z42.object({
|
|
3795
|
+
sessionId: z42.string(),
|
|
3796
|
+
cols: z42.number().int().positive().max(PTY_MAX_DIMENSION),
|
|
3797
|
+
rows: z42.number().int().positive().max(PTY_MAX_DIMENSION)
|
|
3798
|
+
});
|
|
3799
|
+
var PtyAttachRequestSchema = z42.object({
|
|
3800
|
+
sessionId: z42.string()
|
|
3801
|
+
});
|
|
3802
|
+
var ReportPtyStreamRequestSchema = z42.object({
|
|
3803
|
+
sessionId: z42.string(),
|
|
3804
|
+
port: z42.number().int().positive().max(65535).nullable()
|
|
3805
|
+
});
|
|
3806
|
+
var GetPtyStreamEndpointRequestSchema = z42.object({
|
|
3807
|
+
sessionId: z42.string()
|
|
3808
|
+
});
|
|
3809
|
+
var PtyChatEventPayloadSchema = z42.discriminatedUnion("kind", [
|
|
3810
|
+
z42.object({
|
|
3811
|
+
kind: z42.literal("init"),
|
|
3812
|
+
model: z42.string().max(200),
|
|
3813
|
+
claudeSessionId: z42.string().max(100).optional()
|
|
3814
|
+
}),
|
|
3815
|
+
z42.object({
|
|
3816
|
+
kind: z42.literal("user_text"),
|
|
3817
|
+
text: z42.string().max(16384),
|
|
3818
|
+
// Set by the SERVER (never the agent) when this prompt was injected by
|
|
3819
|
+
// Conveyor rather than typed by a human — the routed message's `source`
|
|
3820
|
+
// (`ci_success`, `review_trigger`, `automated_feedback`, …). The agent
|
|
3821
|
+
// pastes automated messages into the TUI exactly like human prompts, so the
|
|
3822
|
+
// CLI records both as plain transcript `user` records; without this the
|
|
3823
|
+
// builder chat renders "All CI checks passed on your PR." as the human's
|
|
3824
|
+
// own bubble. Absent ⇒ a genuine human prompt.
|
|
3825
|
+
source: z42.string().max(60).optional()
|
|
3826
|
+
}),
|
|
3827
|
+
z42.object({ kind: z42.literal("assistant_text"), text: z42.string().max(16384) }),
|
|
3828
|
+
z42.object({
|
|
3829
|
+
kind: z42.literal("tool_use"),
|
|
3830
|
+
name: z42.string().max(200),
|
|
3831
|
+
// Compact preview: JSON.stringify(input) truncated agent-side. The cap
|
|
3832
|
+
// matches the text events because AskUserQuestion payloads ride this field
|
|
3833
|
+
// and the web lifts them into an interactive card — a tight cap forced
|
|
3834
|
+
// option descriptions down to 80 chars, making them unreadable. Every
|
|
3835
|
+
// other tool keeps a far smaller agent-side budget (`TOOL_INPUT_MAX` in
|
|
3836
|
+
// `chat-record-mapper.ts`), so the ring does not grow for normal calls.
|
|
3837
|
+
input: z42.string().max(16384),
|
|
3838
|
+
// Transcript tool_use block id — lets the client pair the tool_result.
|
|
3839
|
+
id: z42.string().max(100).optional()
|
|
3840
|
+
}),
|
|
3841
|
+
z42.object({
|
|
3842
|
+
kind: z42.literal("tool_result"),
|
|
3843
|
+
// tool_use block id this result answers (absent on malformed records).
|
|
3844
|
+
toolUseId: z42.string().max(100).optional(),
|
|
3845
|
+
// Compact output preview, truncated agent-side.
|
|
3846
|
+
output: z42.string().max(2e3),
|
|
3847
|
+
isError: z42.boolean().optional()
|
|
3848
|
+
}),
|
|
3849
|
+
z42.object({ kind: z42.literal("turn_end") })
|
|
3850
|
+
]);
|
|
3851
|
+
var PtyChatEventRequestSchema = z42.object({
|
|
3852
|
+
sessionId: z42.string(),
|
|
3853
|
+
event: PtyChatEventPayloadSchema
|
|
3854
|
+
});
|
|
3855
|
+
var PtyChatAttachRequestSchema = z42.object({
|
|
3856
|
+
sessionId: z42.string()
|
|
3857
|
+
});
|
|
3858
|
+
var CreatePRResponseSchema = z42.object({
|
|
3859
|
+
prNumber: z42.number().int().positive(),
|
|
3860
|
+
prUrl: z42.string().url(),
|
|
3861
|
+
/** Advisory glossary-upkeep note derived from the PR's changed files matched
|
|
3862
|
+
* against tag contextPaths — rendered into the tool result, never stored. */
|
|
3863
|
+
glossaryNote: z42.string().optional()
|
|
3864
|
+
});
|
|
3865
|
+
var PostToChatResponseSchema = z42.object({
|
|
3866
|
+
messageId: z42.string()
|
|
3867
|
+
});
|
|
3868
|
+
var UpdateTaskStatusResponseSchema = z42.object({
|
|
3869
|
+
taskId: z42.string(),
|
|
3870
|
+
status: z42.string()
|
|
3871
|
+
});
|
|
3872
|
+
var StoreSessionIdResponseSchema = z42.object({
|
|
3873
|
+
success: z42.boolean()
|
|
3874
|
+
});
|
|
3875
|
+
var HeartbeatResponseSchema = z42.object({
|
|
3876
|
+
acknowledged: z42.boolean()
|
|
3877
|
+
});
|
|
3878
|
+
var SessionStartResponseSchema = z42.object({
|
|
3879
|
+
sessionId: z42.string(),
|
|
3880
|
+
startedAt: z42.string()
|
|
3881
|
+
});
|
|
3882
|
+
var SessionStopResponseSchema = z42.object({
|
|
3883
|
+
sessionId: z42.string(),
|
|
3884
|
+
stoppedAt: z42.string()
|
|
3885
|
+
});
|
|
3886
|
+
var DeleteSubtaskResponseSchema = z42.object({
|
|
3887
|
+
deleted: z42.boolean()
|
|
3888
|
+
});
|
|
3889
|
+
var GIT_BRANCH_NAME_MAX = 255;
|
|
3890
|
+
var GIT_BRANCH_NAME_MESSAGE = "Invalid git branch name \u2014 use only letters, numbers, '.', '_', '/' and '-', starting with a letter or number, with no '..', '@{' or '//', and no trailing '/', '-', '.' or '.lock'";
|
|
3891
|
+
var ALLOWED_REF = /^[A-Za-z0-9][A-Za-z0-9._/-]*$/;
|
|
3892
|
+
function isValidGitBranchName(name) {
|
|
3893
|
+
if (typeof name !== "string") return false;
|
|
3894
|
+
if (name.length === 0 || name.length > GIT_BRANCH_NAME_MAX) return false;
|
|
3895
|
+
if (!ALLOWED_REF.test(name)) return false;
|
|
3896
|
+
if (name.includes("..") || name.includes("@{") || name.includes("//")) return false;
|
|
3897
|
+
if (name.endsWith("/") || name.endsWith("-")) return false;
|
|
3898
|
+
if (name.endsWith(".") || name.endsWith(".lock")) return false;
|
|
3899
|
+
return true;
|
|
2471
3900
|
}
|
|
2472
|
-
|
|
2473
|
-
|
|
2474
|
-
|
|
2475
|
-
|
|
2476
|
-
|
|
2477
|
-
|
|
2478
|
-
|
|
3901
|
+
var cardDescription2 = z52.string().max(CARD_DESCRIPTION_MAX, CARD_DESCRIPTION_LIMIT_MESSAGE).optional();
|
|
3902
|
+
var ListAccessibleProjectsRequestSchema = z52.object({
|
|
3903
|
+
pageSize: z52.number().int().positive().max(100).optional().default(100)
|
|
3904
|
+
});
|
|
3905
|
+
var ListProjectTasksRequestSchema = z52.object({
|
|
3906
|
+
projectId: z52.string(),
|
|
3907
|
+
status: z52.string().optional(),
|
|
3908
|
+
// Card types to include. Omitted/empty → defaults to ["task"] in the handler
|
|
3909
|
+
// (mirrors searchProjectTasks) so listing doesn't surface incidents/suggestions
|
|
3910
|
+
// unless asked. Enum validation lives at the MCP tool layer.
|
|
3911
|
+
typeFilters: z52.array(z52.string()).optional(),
|
|
3912
|
+
assigneeId: z52.string().optional(),
|
|
3913
|
+
unassigned: z52.boolean().optional(),
|
|
3914
|
+
// Scope to a sub-project board when provided. Unlike the board layer's `?? null`
|
|
3915
|
+
// semantics, agents default to seeing the whole project when omitted.
|
|
3916
|
+
subProjectId: z52.string().nullable().optional(),
|
|
3917
|
+
limit: z52.number().int().positive().optional().default(50)
|
|
3918
|
+
}).refine((p) => !(p.unassigned && p.assigneeId), {
|
|
3919
|
+
message: "Pass either assigneeId or unassigned, not both"
|
|
3920
|
+
});
|
|
3921
|
+
var GetProjectTaskRequestSchema = z52.object({
|
|
3922
|
+
projectId: z52.string(),
|
|
3923
|
+
taskId: z52.string()
|
|
3924
|
+
});
|
|
3925
|
+
var SearchProjectTasksRequestSchema = z52.object({
|
|
3926
|
+
projectId: z52.string(),
|
|
3927
|
+
// Tag names, matched case-insensitively against the project glossary.
|
|
3928
|
+
tagNames: z52.array(z52.string()).optional(),
|
|
3929
|
+
// How to combine tagNames: "any" (default) = carries at least one,
|
|
3930
|
+
// "all" = carries every one.
|
|
3931
|
+
tagMatch: z52.enum(["any", "all"]).optional(),
|
|
3932
|
+
// Expand each named tag to its descendants in the tag DAG before matching,
|
|
3933
|
+
// so a parent tag sweeps its whole area. Default false.
|
|
3934
|
+
includeChildTags: z52.boolean().optional(),
|
|
3935
|
+
searchQuery: z52.string().optional(),
|
|
3936
|
+
statusFilters: z52.array(z52.string()).optional(),
|
|
3937
|
+
// Card types to include. Omitted/empty → defaults to ["task"] in the handler so
|
|
3938
|
+
// search doesn't surface incidents/suggestions unless asked. Enum validation lives
|
|
3939
|
+
// at the MCP tool layer (mirrors statusFilters).
|
|
3940
|
+
typeFilters: z52.array(z52.string()).optional(),
|
|
3941
|
+
assigneeId: z52.string().optional(),
|
|
3942
|
+
unassigned: z52.boolean().optional(),
|
|
3943
|
+
// Scope to a sub-project board when provided. Unlike the board layer's `?? null`
|
|
3944
|
+
// semantics, agents default to seeing the whole project when omitted.
|
|
3945
|
+
subProjectId: z52.string().nullable().optional(),
|
|
3946
|
+
limit: z52.number().int().positive().optional().default(20)
|
|
3947
|
+
}).refine((p) => !(p.unassigned && p.assigneeId), {
|
|
3948
|
+
message: "Pass either assigneeId or unassigned, not both"
|
|
3949
|
+
});
|
|
3950
|
+
var ListProjectTagsRequestSchema = z52.object({
|
|
3951
|
+
projectId: z52.string()
|
|
3952
|
+
});
|
|
3953
|
+
var GetProjectTagRequestSchema = z52.object({
|
|
3954
|
+
projectId: z52.string(),
|
|
3955
|
+
/** Tag id or exact (case-insensitive) tag name. */
|
|
3956
|
+
tag: z52.string().min(1).max(100)
|
|
3957
|
+
});
|
|
3958
|
+
var ListProjectTagAttachmentsRequestSchema = z52.object({
|
|
3959
|
+
projectId: z52.string(),
|
|
3960
|
+
/** Tag id or exact (case-insensitive) tag name. */
|
|
3961
|
+
tag: z52.string().min(1).max(100),
|
|
3962
|
+
limit: z52.number().int().min(1).max(60).optional(),
|
|
3963
|
+
offset: z52.number().int().min(0).optional()
|
|
3964
|
+
});
|
|
3965
|
+
var SetProjectFileTagsRequestSchema = z52.object({
|
|
3966
|
+
projectId: z52.string(),
|
|
3967
|
+
taskId: z52.string(),
|
|
3968
|
+
fileId: z52.string(),
|
|
3969
|
+
tags: z52.array(z52.string().min(1).max(MAX_FILE_TAG_LENGTH)).max(MAX_FILE_TAGS2),
|
|
3970
|
+
requestingUserId: z52.string().optional()
|
|
3971
|
+
});
|
|
3972
|
+
var GetProjectSummaryRequestSchema = z52.object({
|
|
3973
|
+
projectId: z52.string()
|
|
3974
|
+
});
|
|
3975
|
+
var GetProjectOnboardingStatusRequestSchema = z52.object({
|
|
3976
|
+
projectId: z52.string()
|
|
3977
|
+
});
|
|
3978
|
+
var GetProjectOnboardingStepRequestSchema = z52.object({
|
|
3979
|
+
projectId: z52.string()
|
|
3980
|
+
});
|
|
3981
|
+
var GetProjectConnectUrlsRequestSchema = z52.object({
|
|
3982
|
+
projectId: z52.string()
|
|
3983
|
+
});
|
|
3984
|
+
var conveyorCapabilitySchema = z52.enum([
|
|
3985
|
+
"read",
|
|
3986
|
+
"create",
|
|
3987
|
+
"update",
|
|
3988
|
+
"chat",
|
|
3989
|
+
"files",
|
|
3990
|
+
"build"
|
|
3991
|
+
]);
|
|
3992
|
+
var GetConnectionContextRequestSchema = z52.object({
|
|
3993
|
+
projectId: z52.string(),
|
|
3994
|
+
// Optional board scope (CONVEYOR_SUBPROJECT_ID). Validated to belong to the
|
|
3995
|
+
// project in the handler; an invalid/foreign id is reported, not silently
|
|
3996
|
+
// dropped, so a mis-scoped connection is never presented as board-specific.
|
|
3997
|
+
subProjectId: z52.string().nullable().optional()
|
|
3998
|
+
});
|
|
3999
|
+
var VerifyConnectionRequestSchema = z52.object({
|
|
4000
|
+
projectId: z52.string(),
|
|
4001
|
+
subProjectId: z52.string().nullable().optional(),
|
|
4002
|
+
intendedActions: z52.array(conveyorCapabilitySchema).optional()
|
|
4003
|
+
});
|
|
4004
|
+
var ListAccessibleSubprojectsRequestSchema = z52.object({
|
|
4005
|
+
projectId: z52.string()
|
|
4006
|
+
});
|
|
4007
|
+
var CreateProjectTaskRequestSchema = z52.object({
|
|
4008
|
+
projectId: z52.string(),
|
|
4009
|
+
title: z52.string().min(1),
|
|
4010
|
+
description: cardDescription2,
|
|
4011
|
+
plan: z52.string().optional(),
|
|
4012
|
+
status: z52.string().optional(),
|
|
4013
|
+
// Assign to a sub-project board. Validated to belong to `projectId` in the handler.
|
|
4014
|
+
subProjectId: z52.string().nullable().optional(),
|
|
4015
|
+
requestingUserId: z52.string().optional()
|
|
4016
|
+
});
|
|
4017
|
+
var SetProjectTaskParentRequestSchema = z52.object({
|
|
4018
|
+
projectId: z52.string(),
|
|
4019
|
+
/** Card to move — id or slug. */
|
|
4020
|
+
taskId: z52.string().min(1),
|
|
4021
|
+
/** New parent (id or slug), or null to detach. */
|
|
4022
|
+
parentTaskId: z52.string().min(1).nullable(),
|
|
4023
|
+
ordinal: z52.number().int().nonnegative().optional(),
|
|
4024
|
+
followParentStatus: z52.boolean().optional(),
|
|
4025
|
+
requestingUserId: z52.string().optional()
|
|
4026
|
+
}).strict();
|
|
4027
|
+
var UpdateProjectTaskRequestSchema = z52.object({
|
|
4028
|
+
projectId: z52.string(),
|
|
4029
|
+
taskId: z52.string(),
|
|
4030
|
+
title: z52.string().optional(),
|
|
4031
|
+
description: cardDescription2,
|
|
4032
|
+
plan: z52.string().optional(),
|
|
4033
|
+
// Enum validation lives at the MCP tool layer (mirrors createProjectTask);
|
|
4034
|
+
// the handler routes through the shared updateStatus core (InProgress
|
|
4035
|
+
// dependency check + cleanup/board/Slack side effects), not the stricter
|
|
4036
|
+
// card-type-validating path the Socket.IO updateTaskStatus mutation uses.
|
|
4037
|
+
status: z52.string().optional(),
|
|
4038
|
+
// Canonical risk level, or null to clear. Resolved to the project's
|
|
4039
|
+
// configured Risk row (by rank) in the handler.
|
|
4040
|
+
risk: riskLevelSchema.nullable().optional(),
|
|
4041
|
+
// Story-point value, or null to clear. Resolved to the project's configured
|
|
4042
|
+
// StoryPoint row in the handler, which rejects an unconfigured value.
|
|
4043
|
+
storyPointValue: z52.number().int().positive().nullable().optional(),
|
|
4044
|
+
assignedUserId: z52.string().nullish(),
|
|
4045
|
+
// Move to a different sub-project board, or null to move to the parent board.
|
|
4046
|
+
// Validated to belong to `projectId` in the handler.
|
|
4047
|
+
subProjectId: z52.string().nullable().optional(),
|
|
4048
|
+
// Record the task's ACTUAL working branch (e.g. a locally-driven pack's
|
|
4049
|
+
// branch, so identification never mints a competing name and pack-child
|
|
4050
|
+
// merge handling matches reality), or null to detach. Guarded in the
|
|
4051
|
+
// handler: the ref must exist on origin and no live workspace may be bound
|
|
4052
|
+
// to a different branch.
|
|
4053
|
+
githubBranch: z52.string().min(1).max(GIT_BRANCH_NAME_MAX).refine(isValidGitBranchName, { message: GIT_BRANCH_NAME_MESSAGE }).nullable().optional(),
|
|
4054
|
+
requestingUserId: z52.string().optional()
|
|
4055
|
+
}).strict().refine(
|
|
4056
|
+
(v) => v.title !== void 0 || v.description !== void 0 || v.plan !== void 0 || v.status !== void 0 || v.risk !== void 0 || v.storyPointValue !== void 0 || v.assignedUserId !== void 0 || v.subProjectId !== void 0 || v.githubBranch !== void 0,
|
|
4057
|
+
{
|
|
4058
|
+
message: "update_task requires at least one field to change (title, description, plan, status, risk, storyPointValue, assignedUserId, subProjectId, or githubBranch)"
|
|
2479
4059
|
}
|
|
2480
|
-
|
|
2481
|
-
|
|
2482
|
-
|
|
2483
|
-
|
|
2484
|
-
|
|
2485
|
-
|
|
2486
|
-
|
|
2487
|
-
|
|
4060
|
+
);
|
|
4061
|
+
var TransitionProjectTaskStatusRequestSchema = z52.object({
|
|
4062
|
+
projectId: z52.string(),
|
|
4063
|
+
taskId: z52.string(),
|
|
4064
|
+
toStatus: z52.string(),
|
|
4065
|
+
expectedFromStatus: z52.string().optional(),
|
|
4066
|
+
// Optional raise-only risk to attempt alongside the transition
|
|
4067
|
+
// (approve → low, request_changes → medium by default).
|
|
4068
|
+
risk: riskLevelSchema.optional(),
|
|
4069
|
+
requestingUserId: z52.string().optional()
|
|
4070
|
+
});
|
|
4071
|
+
var MoveProjectCardRequestSchema = z52.object({
|
|
4072
|
+
projectId: z52.string(),
|
|
4073
|
+
taskId: z52.string(),
|
|
4074
|
+
destinationProjectId: z52.string(),
|
|
4075
|
+
requestingUserId: z52.string().optional()
|
|
4076
|
+
});
|
|
4077
|
+
var PostToProjectTaskChatRequestSchema = z52.object({
|
|
4078
|
+
projectId: z52.string(),
|
|
4079
|
+
taskId: z52.string(),
|
|
4080
|
+
content: z52.string(),
|
|
4081
|
+
requestingUserId: z52.string().optional()
|
|
4082
|
+
});
|
|
4083
|
+
var GetProjectTaskCliRequestSchema = z52.object({
|
|
4084
|
+
projectId: z52.string(),
|
|
4085
|
+
taskId: z52.string(),
|
|
4086
|
+
limit: z52.number().int().positive().optional().default(50),
|
|
4087
|
+
source: z52.string().optional()
|
|
4088
|
+
});
|
|
4089
|
+
var GetProjectTaskSessionsRequestSchema = z52.object({
|
|
4090
|
+
projectId: z52.string(),
|
|
4091
|
+
taskId: z52.string()
|
|
4092
|
+
});
|
|
4093
|
+
var QueryProjectGcpLogsRequestSchema = z52.object({
|
|
4094
|
+
projectId: z52.string(),
|
|
4095
|
+
env: z52.enum(["prod", "dev", "claudespace"]).optional(),
|
|
4096
|
+
severity: z52.enum(["DEBUG", "INFO", "NOTICE", "WARNING", "ERROR", "CRITICAL", "ALERT", "EMERGENCY"]).optional(),
|
|
4097
|
+
services: z52.array(z52.string().min(1).max(200)).max(25).optional(),
|
|
4098
|
+
sqlInstances: z52.array(z52.string().min(1).max(200)).max(25).optional(),
|
|
4099
|
+
allServices: z52.boolean().optional(),
|
|
4100
|
+
search: z52.string().max(256).optional(),
|
|
4101
|
+
filter: z52.string().max(1e3).optional(),
|
|
4102
|
+
startTime: z52.string().optional(),
|
|
4103
|
+
endTime: z52.string().optional(),
|
|
4104
|
+
limit: z52.number().int().min(1).max(200).optional().default(50),
|
|
4105
|
+
pageToken: z52.string().max(4096).optional()
|
|
4106
|
+
});
|
|
4107
|
+
var QueryProjectGrafanaLogsRequestSchema = z52.object({
|
|
4108
|
+
projectId: z52.string(),
|
|
4109
|
+
env: z52.enum(["prod", "dev"]).optional(),
|
|
4110
|
+
services: z52.array(z52.string().min(1).max(200)).max(25).optional(),
|
|
4111
|
+
level: z52.enum(["debug", "info", "warn", "error", "fatal"]).optional(),
|
|
4112
|
+
search: z52.string().max(256).optional(),
|
|
4113
|
+
logql: z52.string().max(2e3).optional(),
|
|
4114
|
+
startTime: z52.string().optional(),
|
|
4115
|
+
endTime: z52.string().optional(),
|
|
4116
|
+
limit: z52.number().int().min(1).max(200).optional().default(50)
|
|
4117
|
+
});
|
|
4118
|
+
var driveFileNameSchema = z52.string().min(1).max(255).regex(/^[^/\\\r\n]+$/, "File names cannot contain slashes or line breaks");
|
|
4119
|
+
var DRIVE_MAX_CONTENT_CHARS = 1e6;
|
|
4120
|
+
var ListProjectDriveFilesRequestSchema = z52.object({
|
|
4121
|
+
projectId: z52.string(),
|
|
4122
|
+
folderId: z52.string().max(200).optional(),
|
|
4123
|
+
search: z52.string().max(200).optional(),
|
|
4124
|
+
limit: z52.number().int().min(1).max(200).optional()
|
|
4125
|
+
});
|
|
4126
|
+
var ReadProjectDriveFileRequestSchema = z52.object({
|
|
4127
|
+
projectId: z52.string(),
|
|
4128
|
+
fileId: z52.string().min(1).max(200)
|
|
4129
|
+
});
|
|
4130
|
+
var CreateProjectDriveFileRequestSchema = z52.object({
|
|
4131
|
+
projectId: z52.string(),
|
|
4132
|
+
name: driveFileNameSchema,
|
|
4133
|
+
content: z52.string().max(DRIVE_MAX_CONTENT_CHARS),
|
|
4134
|
+
mimeType: z52.string().max(200).optional(),
|
|
4135
|
+
folderId: z52.string().max(200).optional()
|
|
4136
|
+
});
|
|
4137
|
+
var UpdateProjectDriveFileRequestSchema = z52.object({
|
|
4138
|
+
projectId: z52.string(),
|
|
4139
|
+
fileId: z52.string().min(1).max(200),
|
|
4140
|
+
content: z52.string().max(DRIVE_MAX_CONTENT_CHARS),
|
|
4141
|
+
mimeType: z52.string().max(200).optional()
|
|
4142
|
+
});
|
|
4143
|
+
var DeleteProjectDriveFileRequestSchema = z52.object({
|
|
4144
|
+
projectId: z52.string(),
|
|
4145
|
+
fileId: z52.string().min(1).max(200)
|
|
4146
|
+
});
|
|
4147
|
+
var CreateProjectDriveFolderRequestSchema = z52.object({
|
|
4148
|
+
projectId: z52.string(),
|
|
4149
|
+
name: driveFileNameSchema,
|
|
4150
|
+
folderId: z52.string().max(200).optional()
|
|
4151
|
+
});
|
|
4152
|
+
var StartProjectBuildRequestSchema = z52.object({
|
|
4153
|
+
projectId: z52.string(),
|
|
4154
|
+
taskId: z52.string(),
|
|
4155
|
+
requestingUserId: z52.string().optional()
|
|
4156
|
+
});
|
|
4157
|
+
var StopProjectBuildRequestSchema = z52.object({
|
|
4158
|
+
projectId: z52.string(),
|
|
4159
|
+
taskId: z52.string(),
|
|
4160
|
+
requestingUserId: z52.string().optional()
|
|
4161
|
+
});
|
|
4162
|
+
var StartProjectWorkspaceRequestSchema = z52.object({
|
|
4163
|
+
projectId: z52.string(),
|
|
4164
|
+
requestingUserId: z52.string().optional()
|
|
4165
|
+
});
|
|
4166
|
+
var StopProjectWorkspaceRequestSchema = z52.object({
|
|
4167
|
+
projectId: z52.string(),
|
|
4168
|
+
destroy: z52.boolean().optional(),
|
|
4169
|
+
requestingUserId: z52.string().optional()
|
|
4170
|
+
});
|
|
4171
|
+
var ListMyLiveSessionsRequestSchema = z52.object({
|
|
4172
|
+
projectId: z52.string(),
|
|
4173
|
+
/** Admin-only: list another member's sessions instead of the caller's. */
|
|
4174
|
+
targetUserId: z52.string().optional()
|
|
4175
|
+
});
|
|
4176
|
+
var ListProjectSessionGroupsRequestSchema = z52.object({
|
|
4177
|
+
projectId: z52.string()
|
|
4178
|
+
});
|
|
4179
|
+
var ListMyLiveSessionsAcrossProjectsRequestSchema = z52.object({});
|
|
4180
|
+
var GetProjectAvailableTuisRequestSchema = z52.object({
|
|
4181
|
+
projectId: z52.string()
|
|
4182
|
+
});
|
|
4183
|
+
var StartAdhocSessionRequestSchema = z52.object({
|
|
4184
|
+
projectId: z52.string(),
|
|
4185
|
+
label: z52.string().max(200).optional(),
|
|
4186
|
+
/** Coding-agent key to launch under — validated pick-time (ownership + TUI availability) in the handler. */
|
|
4187
|
+
codingAgentKeyId: z52.string().optional(),
|
|
4188
|
+
/** Model override (Claude model id) — overrides the launch key's own model. */
|
|
4189
|
+
model: z52.string().max(200).optional(),
|
|
4190
|
+
/**
|
|
4191
|
+
* Session role. Constrained: other task-less modes fall through to the pm
|
|
4192
|
+
* runner in the pod entrypoint, and "review" would crash without a task.
|
|
4193
|
+
*/
|
|
4194
|
+
mode: z52.enum(["adhoc", "pm"]).optional(),
|
|
4195
|
+
/** Base branch to check out (defaults to the project's dev branch). */
|
|
4196
|
+
branch: z52.string().max(300).optional(),
|
|
4197
|
+
/**
|
|
4198
|
+
* Server-assembled instructions the pod's TUI auto-submits once on first boot
|
|
4199
|
+
* (headless kickoff). Used by the onboarding "Set it up for me" flow to seed a
|
|
4200
|
+
* setup-driver prompt; the session stays watchable/interactive in the Sessions
|
|
4201
|
+
* view. `ensureAdhocWorkspace` persists it and clears it after first submit.
|
|
4202
|
+
*/
|
|
4203
|
+
initialPrompt: z52.string().max(2e4).optional(),
|
|
4204
|
+
requestingUserId: z52.string().optional()
|
|
4205
|
+
});
|
|
4206
|
+
var StopAdhocSessionRequestSchema = z52.object({
|
|
4207
|
+
projectId: z52.string(),
|
|
4208
|
+
workspaceId: z52.string(),
|
|
4209
|
+
destroy: z52.boolean().optional(),
|
|
4210
|
+
requestingUserId: z52.string().optional()
|
|
4211
|
+
});
|
|
4212
|
+
var ResumeAdhocSessionRequestSchema = z52.object({
|
|
4213
|
+
projectId: z52.string(),
|
|
4214
|
+
workspaceId: z52.string(),
|
|
4215
|
+
requestingUserId: z52.string().optional()
|
|
4216
|
+
});
|
|
4217
|
+
var RefreshCodingAgentKeyUsageRequestSchema = z52.object({
|
|
4218
|
+
projectId: z52.string(),
|
|
4219
|
+
keyId: z52.string().optional(),
|
|
4220
|
+
requestingUserId: z52.string().optional()
|
|
4221
|
+
});
|
|
4222
|
+
var ListKeysToProbeRequestSchema = z52.object({
|
|
4223
|
+
sessionId: z52.string()
|
|
4224
|
+
});
|
|
4225
|
+
var CreateProjectReleaseRequestSchema = z52.object({
|
|
4226
|
+
projectId: z52.string(),
|
|
4227
|
+
taskIds: z52.array(z52.string()).optional(),
|
|
4228
|
+
requestingUserId: z52.string().optional()
|
|
4229
|
+
});
|
|
4230
|
+
var AddTasksToProjectReleaseRequestSchema = z52.object({
|
|
4231
|
+
projectId: z52.string(),
|
|
4232
|
+
taskIds: z52.array(z52.string()).min(1),
|
|
4233
|
+
requestingUserId: z52.string().optional()
|
|
4234
|
+
});
|
|
4235
|
+
var ApproveProjectMergePRRequestSchema = z52.object({
|
|
4236
|
+
projectId: z52.string(),
|
|
4237
|
+
childTaskId: z52.string(),
|
|
4238
|
+
requestingUserId: z52.string().optional()
|
|
4239
|
+
});
|
|
4240
|
+
var ListProjectSubtasksRequestSchema = z52.object({
|
|
4241
|
+
projectId: z52.string(),
|
|
4242
|
+
taskId: z52.string()
|
|
4243
|
+
});
|
|
4244
|
+
var CreateProjectSubtaskRequestSchema = z52.object({
|
|
4245
|
+
projectId: z52.string(),
|
|
4246
|
+
parentTaskId: z52.string(),
|
|
4247
|
+
title: z52.string().min(1),
|
|
4248
|
+
description: cardDescription2,
|
|
4249
|
+
plan: z52.string().optional(),
|
|
4250
|
+
ordinal: z52.number().int().nonnegative().optional(),
|
|
4251
|
+
storyPointValue: z52.number().int().positive().optional(),
|
|
4252
|
+
followParentStatus: z52.boolean().optional(),
|
|
4253
|
+
/** Sibling subtask ids or slugs this subtask blocks on (explicit dependency
|
|
4254
|
+
* metadata — preferred over encoding order in plan text / ordinal). */
|
|
4255
|
+
dependsOn: z52.array(z52.string().min(1)).max(32).optional(),
|
|
4256
|
+
requestingUserId: z52.string().optional()
|
|
4257
|
+
});
|
|
4258
|
+
var UpdateProjectSubtaskRequestSchema = z52.object({
|
|
4259
|
+
projectId: z52.string(),
|
|
4260
|
+
subtaskId: z52.string(),
|
|
4261
|
+
title: z52.string().optional(),
|
|
4262
|
+
description: cardDescription2,
|
|
4263
|
+
plan: z52.string().optional(),
|
|
4264
|
+
status: z52.string().optional(),
|
|
4265
|
+
ordinal: z52.number().int().nonnegative().optional(),
|
|
4266
|
+
storyPointValue: z52.number().int().positive().optional(),
|
|
4267
|
+
followParentStatus: z52.boolean().optional(),
|
|
4268
|
+
/** Replace-set of sibling subtask ids/slugs this subtask blocks on ([] clears).
|
|
4269
|
+
* Mirrors the in-pod updateSubtask semantics. */
|
|
4270
|
+
dependsOn: z52.array(z52.string().min(1)).max(32).optional(),
|
|
4271
|
+
requestingUserId: z52.string().optional()
|
|
4272
|
+
});
|
|
4273
|
+
var DeleteProjectSubtaskRequestSchema = z52.object({
|
|
4274
|
+
projectId: z52.string(),
|
|
4275
|
+
subtaskId: z52.string(),
|
|
4276
|
+
requestingUserId: z52.string().optional()
|
|
4277
|
+
});
|
|
4278
|
+
var GetProjectTaskChatRequestSchema = z52.object({
|
|
4279
|
+
projectId: z52.string(),
|
|
4280
|
+
taskId: z52.string(),
|
|
4281
|
+
limit: z52.number().int().positive().optional().default(20)
|
|
4282
|
+
});
|
|
4283
|
+
var AddProjectTaskDependencyRequestSchema = z52.object({
|
|
4284
|
+
projectId: z52.string(),
|
|
4285
|
+
taskId: z52.string(),
|
|
4286
|
+
dependsOnSlugOrId: z52.string(),
|
|
4287
|
+
requestingUserId: z52.string().optional()
|
|
4288
|
+
});
|
|
4289
|
+
var RemoveProjectTaskDependencyRequestSchema = z52.object({
|
|
4290
|
+
projectId: z52.string(),
|
|
4291
|
+
taskId: z52.string(),
|
|
4292
|
+
dependsOnSlugOrId: z52.string(),
|
|
4293
|
+
requestingUserId: z52.string().optional()
|
|
4294
|
+
});
|
|
4295
|
+
var VoteProjectSuggestionRequestSchema = z52.object({
|
|
4296
|
+
projectId: z52.string(),
|
|
4297
|
+
suggestionId: z52.string(),
|
|
4298
|
+
value: z52.union([z52.literal(1), z52.literal(-1)]),
|
|
4299
|
+
requestingUserId: z52.string().optional()
|
|
4300
|
+
});
|
|
4301
|
+
var GetProjectTaskDependenciesRequestSchema = z52.object({
|
|
4302
|
+
projectId: z52.string(),
|
|
4303
|
+
taskId: z52.string()
|
|
4304
|
+
});
|
|
4305
|
+
var ListProjectTaskFilesRequestSchema = z52.object({
|
|
4306
|
+
projectId: z52.string(),
|
|
4307
|
+
taskId: z52.string()
|
|
4308
|
+
});
|
|
4309
|
+
var GetProjectAttachmentRequestSchema = z52.object({
|
|
4310
|
+
projectId: z52.string(),
|
|
4311
|
+
taskId: z52.string(),
|
|
4312
|
+
fileId: z52.string(),
|
|
4313
|
+
/** Byte offset into text content (paging large logs/JSON). Default 0. */
|
|
4314
|
+
offset: z52.number().int().nonnegative().optional(),
|
|
4315
|
+
/** Max bytes of text content to return from `offset`. Server default applies. */
|
|
4316
|
+
maxBytes: z52.number().int().positive().optional()
|
|
4317
|
+
});
|
|
4318
|
+
var RequestProjectFileUploadRequestSchema = z52.object({
|
|
4319
|
+
projectId: z52.string(),
|
|
4320
|
+
taskId: z52.string(),
|
|
4321
|
+
fileName: z52.string().min(1).max(255),
|
|
4322
|
+
mimeType: z52.string().min(1).max(128),
|
|
4323
|
+
fileSize: z52.number().int().positive().max(MAX_FILE_SIZE_BYTES2),
|
|
4324
|
+
requestingUserId: z52.string().optional()
|
|
4325
|
+
});
|
|
4326
|
+
var ConfirmProjectFileUploadRequestSchema = z52.object({
|
|
4327
|
+
projectId: z52.string(),
|
|
4328
|
+
taskId: z52.string(),
|
|
4329
|
+
fileId: z52.string(),
|
|
4330
|
+
/** When set, the attachment is also posted to the task chat with this text. */
|
|
4331
|
+
comment: z52.string().max(2e3).optional(),
|
|
4332
|
+
/** Glossary tag names (or ids) this file is an example of. */
|
|
4333
|
+
tags: z52.array(z52.string().min(1).max(MAX_FILE_TAG_LENGTH)).max(MAX_FILE_TAGS2).optional(),
|
|
4334
|
+
requestingUserId: z52.string().optional()
|
|
4335
|
+
});
|
|
4336
|
+
var CreateProjectPullRequestRequestSchema = z52.object({
|
|
4337
|
+
projectId: z52.string(),
|
|
4338
|
+
taskId: z52.string(),
|
|
4339
|
+
title: z52.string().min(1),
|
|
4340
|
+
body: z52.string(),
|
|
4341
|
+
head: z52.string().optional(),
|
|
4342
|
+
base: z52.string().optional(),
|
|
4343
|
+
requestingUserId: z52.string().optional()
|
|
4344
|
+
});
|
|
4345
|
+
var ListProjectMembersRequestSchema = z52.object({
|
|
4346
|
+
projectId: z52.string()
|
|
4347
|
+
});
|
|
4348
|
+
var AddProjectTaskReviewerRequestSchema = z52.object({
|
|
4349
|
+
projectId: z52.string(),
|
|
4350
|
+
taskId: z52.string(),
|
|
4351
|
+
userId: z52.string(),
|
|
4352
|
+
requestingUserId: z52.string().optional()
|
|
4353
|
+
});
|
|
4354
|
+
var RemoveProjectTaskReviewerRequestSchema = z52.object({
|
|
4355
|
+
projectId: z52.string(),
|
|
4356
|
+
taskId: z52.string(),
|
|
4357
|
+
userId: z52.string(),
|
|
4358
|
+
requestingUserId: z52.string().optional()
|
|
4359
|
+
});
|
|
4360
|
+
var ListProjectManualTestsRequestSchema = z52.object({
|
|
4361
|
+
projectId: z52.string(),
|
|
4362
|
+
taskId: z52.string()
|
|
4363
|
+
});
|
|
4364
|
+
var QueryProjectManualTestsRequestSchema = z52.object({
|
|
4365
|
+
projectId: z52.string(),
|
|
4366
|
+
cardStatuses: z52.array(z52.string()).optional(),
|
|
4367
|
+
testStatuses: z52.array(z52.enum(["open", "approved", "rejected"])).optional()
|
|
4368
|
+
});
|
|
4369
|
+
var SetProjectManualTestsRequestSchema = z52.object({
|
|
4370
|
+
projectId: z52.string(),
|
|
4371
|
+
taskId: z52.string(),
|
|
4372
|
+
items: z52.array(z52.object({ title: z52.string().min(1) })).min(1),
|
|
4373
|
+
requestingUserId: z52.string().optional()
|
|
4374
|
+
});
|
|
4375
|
+
var EditProjectManualTestRequestSchema = z52.object({
|
|
4376
|
+
projectId: z52.string(),
|
|
4377
|
+
taskId: z52.string(),
|
|
4378
|
+
title: z52.string().min(1),
|
|
4379
|
+
newTitle: z52.string().min(1),
|
|
4380
|
+
requestingUserId: z52.string().optional()
|
|
4381
|
+
});
|
|
4382
|
+
var RemoveProjectManualTestRequestSchema = z52.object({
|
|
4383
|
+
projectId: z52.string(),
|
|
4384
|
+
taskId: z52.string(),
|
|
4385
|
+
title: z52.string().min(1),
|
|
4386
|
+
requestingUserId: z52.string().optional()
|
|
4387
|
+
});
|
|
4388
|
+
var ApproveProjectManualTestRequestSchema = z52.object({
|
|
4389
|
+
projectId: z52.string(),
|
|
4390
|
+
taskId: z52.string(),
|
|
4391
|
+
title: z52.string().min(1),
|
|
4392
|
+
requestingUserId: z52.string().optional()
|
|
4393
|
+
});
|
|
4394
|
+
var RejectProjectManualTestRequestSchema = z52.object({
|
|
4395
|
+
projectId: z52.string(),
|
|
4396
|
+
taskId: z52.string(),
|
|
4397
|
+
title: z52.string().min(1),
|
|
4398
|
+
reason: z52.string().min(1).max(2e3),
|
|
4399
|
+
requestingUserId: z52.string().optional()
|
|
4400
|
+
});
|
|
4401
|
+
var CreateProjectSuggestionRequestSchema = z52.object({
|
|
4402
|
+
projectId: z52.string(),
|
|
4403
|
+
title: z52.string().min(1),
|
|
4404
|
+
description: cardDescription2,
|
|
4405
|
+
tagNames: z52.array(z52.string()).optional(),
|
|
4406
|
+
requestingUserId: z52.string().optional()
|
|
4407
|
+
});
|
|
4408
|
+
var ListProjectChannelsRequestSchema = z62.object({
|
|
4409
|
+
projectId: z62.string()
|
|
4410
|
+
});
|
|
4411
|
+
var READ_CHANNEL_MESSAGES_MAX_LIMIT = 50;
|
|
4412
|
+
var ReadChannelMessagesRequestSchema = z62.object({
|
|
4413
|
+
projectId: z62.string(),
|
|
4414
|
+
channelId: z62.string().min(1).max(200),
|
|
4415
|
+
limit: z62.number().int().min(1).max(READ_CHANNEL_MESSAGES_MAX_LIMIT).optional(),
|
|
4416
|
+
/** Provider-native cursor: return messages OLDER than this one. */
|
|
4417
|
+
before: z62.string().max(100).optional(),
|
|
4418
|
+
/** Provider-native cursor: return messages NEWER than this one. */
|
|
4419
|
+
after: z62.string().max(100).optional(),
|
|
4420
|
+
/**
|
|
4421
|
+
* Read one thread instead of the channel surface. Slack calls this a
|
|
4422
|
+
* `thread_ts`; on Discord it is the thread channel's id. One field for both,
|
|
4423
|
+
* because a caller holding a `threadTs` from a previous read should not have
|
|
4424
|
+
* to know which provider produced it.
|
|
4425
|
+
*/
|
|
4426
|
+
threadTs: z62.string().max(100).optional()
|
|
4427
|
+
});
|
|
4428
|
+
var POST_CHANNEL_MESSAGE_MAX_CHARS = 1800;
|
|
4429
|
+
var PostChannelMessageRequestSchema = z62.object({
|
|
4430
|
+
projectId: z62.string(),
|
|
4431
|
+
channelId: z62.string().min(1).max(200),
|
|
4432
|
+
text: z62.string().min(1).max(POST_CHANNEL_MESSAGE_MAX_CHARS),
|
|
4433
|
+
/** Reply inside a thread rather than to the channel. */
|
|
4434
|
+
threadTs: z62.string().max(100).optional()
|
|
4435
|
+
});
|
|
4436
|
+
var GET_ANALYTICS_SUMMARY_MAX_RANGE_DAYS = 90;
|
|
4437
|
+
var GetProjectAnalyticsSummaryRequestSchema = z62.object({
|
|
4438
|
+
projectId: z62.string(),
|
|
4439
|
+
rangeDays: z62.number().int().min(1).max(GET_ANALYTICS_SUMMARY_MAX_RANGE_DAYS).optional(),
|
|
4440
|
+
campaign: z62.string().max(200).optional()
|
|
4441
|
+
});
|
|
4442
|
+
var SHA_PATTERN = /^[0-9a-f]{40}$/i;
|
|
4443
|
+
var ReviewGuideFileReferenceSchema = z72.object({
|
|
4444
|
+
path: z72.string().min(1).max(500),
|
|
4445
|
+
startLine: z72.number().int().positive().max(1e6).optional(),
|
|
4446
|
+
endLine: z72.number().int().positive().max(1e6).optional(),
|
|
4447
|
+
hunkHeader: z72.string().min(1).max(300).optional()
|
|
4448
|
+
}).strict().superRefine((value, ctx) => {
|
|
4449
|
+
if (value.endLine !== void 0 && value.startLine === void 0) {
|
|
4450
|
+
ctx.addIssue({
|
|
4451
|
+
code: "custom",
|
|
4452
|
+
path: ["startLine"],
|
|
4453
|
+
message: "startLine is required when endLine is set"
|
|
4454
|
+
});
|
|
4455
|
+
}
|
|
4456
|
+
if (value.startLine !== void 0 && value.endLine !== void 0 && value.endLine < value.startLine) {
|
|
4457
|
+
ctx.addIssue({
|
|
4458
|
+
code: "custom",
|
|
4459
|
+
path: ["endLine"],
|
|
4460
|
+
message: "endLine must be greater than or equal to startLine"
|
|
4461
|
+
});
|
|
4462
|
+
}
|
|
4463
|
+
});
|
|
4464
|
+
var ReviewGuideSectionSchema = z72.object({
|
|
4465
|
+
title: z72.string().min(1).max(160),
|
|
4466
|
+
explanation: z72.string().min(1).max(2e3),
|
|
4467
|
+
classification: z72.enum(["core", "supporting"]).optional(),
|
|
4468
|
+
files: z72.array(ReviewGuideFileReferenceSchema).min(1).max(20)
|
|
4469
|
+
}).strict();
|
|
4470
|
+
var ReviewGuideContentSchema = z72.object({
|
|
4471
|
+
overview: z72.string().min(1).max(3e3),
|
|
4472
|
+
sections: z72.array(ReviewGuideSectionSchema).min(1).max(12)
|
|
4473
|
+
}).strict();
|
|
4474
|
+
var PublishReviewGuideRequestSchema = ReviewGuideContentSchema.extend({
|
|
4475
|
+
sessionId: z72.string().min(1),
|
|
4476
|
+
reviewedSha: z72.string().regex(SHA_PATTERN, "reviewedSha must be a 40-character commit SHA")
|
|
4477
|
+
}).strict();
|
|
4478
|
+
var CONTEXT_LINK_LOCATOR_MAX2 = 300;
|
|
4479
|
+
var TAG_DESCRIPTION_MAX = CARD_DESCRIPTION_MAX;
|
|
4480
|
+
var TAG_OVERVIEW_MAX = 32e3;
|
|
4481
|
+
var TAG_REASON_MAX = 500;
|
|
4482
|
+
var ProjectTagContextPathSchema = z82.object({
|
|
4483
|
+
type: z82.enum(["rule", "doc", "file", "folder"]),
|
|
4484
|
+
path: z82.string().min(1).max(500),
|
|
4485
|
+
label: z82.string().max(100).optional(),
|
|
4486
|
+
/** Verified-link tether — text that must keep existing in the file. */
|
|
4487
|
+
locator: z82.string().min(1).max(CONTEXT_LINK_LOCATOR_MAX2).regex(/^[^\r\n]*$/, "Locator cannot contain line breaks").optional(),
|
|
4488
|
+
/** test = must appear in a real test/describe title; code = any substring. */
|
|
4489
|
+
locatorType: z82.enum(["test", "code"]).optional()
|
|
4490
|
+
}).refine((link) => link.locator === void 0 === (link.locatorType === void 0), {
|
|
4491
|
+
message: "locator and locatorType must be provided together"
|
|
4492
|
+
}).refine((link) => link.locator === void 0 || link.type !== "folder", {
|
|
4493
|
+
message: "folder links cannot carry a locator"
|
|
4494
|
+
});
|
|
4495
|
+
var hexColor = z82.string().regex(/^#[0-9a-fA-F]{6}$/, "Expected #RRGGBB hex color");
|
|
4496
|
+
var overviewPathSchema = z82.string().min(1).max(500).regex(/^[^\r\n]*$/, "Overview path cannot contain line breaks");
|
|
4497
|
+
var CreateProjectTagRequestSchema = z82.object({
|
|
4498
|
+
projectId: z82.string(),
|
|
4499
|
+
name: z82.string().min(1).max(50),
|
|
4500
|
+
color: hexColor.optional(),
|
|
4501
|
+
description: z82.string().max(TAG_DESCRIPTION_MAX).optional(),
|
|
4502
|
+
overview: z82.string().max(TAG_OVERVIEW_MAX).optional(),
|
|
4503
|
+
/** Source the overview from this repo file (stored overview stays as the pending fallback). */
|
|
4504
|
+
overviewPath: overviewPathSchema.optional(),
|
|
4505
|
+
contextPaths: z82.array(ProjectTagContextPathSchema).max(20).optional(),
|
|
4506
|
+
/** Parents to link at create time (multi-parent DAG). */
|
|
4507
|
+
parentTagIds: z82.array(z82.string()).max(25).optional(),
|
|
4508
|
+
requestingUserId: z82.string().optional()
|
|
4509
|
+
});
|
|
4510
|
+
var UpdateProjectTagRequestSchema = z82.object({
|
|
4511
|
+
projectId: z82.string(),
|
|
4512
|
+
tagId: z82.string(),
|
|
4513
|
+
name: z82.string().min(1).max(50).optional(),
|
|
4514
|
+
color: hexColor.optional(),
|
|
4515
|
+
description: z82.string().max(TAG_DESCRIPTION_MAX).optional(),
|
|
4516
|
+
/** Full markdown glossary body; null clears it. Rejected while overviewPath is set. */
|
|
4517
|
+
overview: z82.string().max(TAG_OVERVIEW_MAX).nullable().optional(),
|
|
4518
|
+
/** Repo file to source the overview from; null clears back to the stored overview. */
|
|
4519
|
+
overviewPath: overviewPathSchema.nullable().optional(),
|
|
4520
|
+
/** Full replacement of the tag's context links when provided. */
|
|
4521
|
+
contextPaths: z82.array(ProjectTagContextPathSchema).max(20).optional(),
|
|
4522
|
+
/** Full-set replacement of the tag's parent tags (multi-parent DAG). */
|
|
4523
|
+
parentTagIds: z82.array(z82.string()).max(25).optional(),
|
|
4524
|
+
/** One-line revision provenance, recorded in the tag's history. */
|
|
4525
|
+
reason: z82.string().max(TAG_REASON_MAX).optional(),
|
|
4526
|
+
/** Card the caller was working in — stamped into the revision history. */
|
|
4527
|
+
taskId: z82.string().optional(),
|
|
4528
|
+
requestingUserId: z82.string().optional()
|
|
4529
|
+
});
|
|
4530
|
+
var PostToProjectChatRequestSchema = z82.object({
|
|
4531
|
+
projectId: z82.string(),
|
|
4532
|
+
content: z82.string().min(1).max(2e4),
|
|
4533
|
+
requestingUserId: z82.string().optional(),
|
|
4534
|
+
/** Marks the post so the server can persist it beyond chat (tag-audit summaries land in tag history). */
|
|
4535
|
+
kind: z82.enum(["tag_audit_summary"]).optional()
|
|
4536
|
+
});
|
|
4537
|
+
var StartTagAuditRequestSchema = z82.object({
|
|
4538
|
+
projectId: z82.string(),
|
|
4539
|
+
requestingUserId: z82.string().optional()
|
|
4540
|
+
});
|
|
4541
|
+
var StartTaskAuditRequestSchema = z82.object({
|
|
4542
|
+
projectId: z82.string(),
|
|
4543
|
+
taskIds: z82.array(z82.string()).min(1).max(20),
|
|
4544
|
+
requestingUserId: z82.string().optional()
|
|
4545
|
+
});
|
|
4546
|
+
var GetActiveAuditSessionsRequestSchema = z82.object({
|
|
4547
|
+
projectId: z82.string()
|
|
4548
|
+
});
|
|
4549
|
+
var ReportTaskAuditResultRequestSchema = z82.object({
|
|
4550
|
+
projectId: z82.string(),
|
|
4551
|
+
taskId: z82.string(),
|
|
4552
|
+
summary: z82.string(),
|
|
4553
|
+
turnGrades: z82.array(
|
|
4554
|
+
z82.object({
|
|
4555
|
+
turnIndex: z82.number(),
|
|
4556
|
+
phase: z82.enum(["planning", "building", "human"]),
|
|
4557
|
+
grade: z82.enum(["correct", "neutral", "blunder"]),
|
|
4558
|
+
reasoning: z82.string(),
|
|
4559
|
+
eventType: z82.string(),
|
|
4560
|
+
eventSummary: z82.string()
|
|
4561
|
+
})
|
|
4562
|
+
),
|
|
4563
|
+
planningAccuracy: z82.number().nullable(),
|
|
4564
|
+
buildingAccuracy: z82.number().nullable(),
|
|
4565
|
+
humanAccuracy: z82.number().nullable(),
|
|
4566
|
+
planningCorrect: z82.number(),
|
|
4567
|
+
planningNeutral: z82.number(),
|
|
4568
|
+
planningBlunder: z82.number(),
|
|
4569
|
+
buildingCorrect: z82.number(),
|
|
4570
|
+
buildingNeutral: z82.number(),
|
|
4571
|
+
buildingBlunder: z82.number(),
|
|
4572
|
+
humanCorrect: z82.number(),
|
|
4573
|
+
humanNeutral: z82.number(),
|
|
4574
|
+
humanBlunder: z82.number(),
|
|
4575
|
+
humanEvaluations: z82.array(
|
|
4576
|
+
z82.object({
|
|
4577
|
+
messageIndex: z82.number(),
|
|
4578
|
+
rating: z82.union([z82.literal(-1), z82.literal(0), z82.literal(1)]),
|
|
4579
|
+
reasoning: z82.string()
|
|
4580
|
+
})
|
|
4581
|
+
).optional(),
|
|
4582
|
+
suggestionIds: z82.array(z82.string()),
|
|
4583
|
+
auditCostUsd: z82.number().nullable(),
|
|
4584
|
+
model: z82.string().nullable(),
|
|
4585
|
+
/** When set, the audit is marked failed with this message instead. */
|
|
4586
|
+
error: z82.string().optional()
|
|
4587
|
+
});
|
|
4588
|
+
var GetTaskAuditsRequestSchema = z82.object({
|
|
4589
|
+
projectId: z82.string(),
|
|
4590
|
+
limit: z82.number().int().positive().max(200).optional().default(50)
|
|
4591
|
+
});
|
|
4592
|
+
var GetTaskAuditRequestSchema = z82.object({
|
|
4593
|
+
projectId: z82.string(),
|
|
4594
|
+
auditId: z82.string()
|
|
4595
|
+
});
|
|
4596
|
+
var GetTaskAuditAggregatesRequestSchema = z82.object({
|
|
4597
|
+
projectId: z82.string()
|
|
4598
|
+
});
|
|
4599
|
+
var DeleteTaskAuditRequestSchema = z82.object({
|
|
4600
|
+
projectId: z82.string(),
|
|
4601
|
+
auditId: z82.string(),
|
|
4602
|
+
requestingUserId: z82.string().optional()
|
|
4603
|
+
});
|
|
4604
|
+
var MarkInitialPromptSubmittedRequestSchema = z82.object({
|
|
4605
|
+
sessionId: z82.string()
|
|
4606
|
+
});
|
|
4607
|
+
var MEETING_TRANSCRIPT_MAX_CHARS = 2e6;
|
|
4608
|
+
var MEETING_TITLE_MAX = 200;
|
|
4609
|
+
var CreateMeetingFromTranscriptRequestSchema = z92.object({
|
|
4610
|
+
projectId: z92.string().cuid(),
|
|
4611
|
+
rawText: z92.string().min(1).max(MEETING_TRANSCRIPT_MAX_CHARS),
|
|
4612
|
+
title: z92.string().min(1).max(MEETING_TITLE_MAX).optional(),
|
|
4613
|
+
/** ISO 8601. Defaults to now when the source carries no date. */
|
|
4614
|
+
occurredAt: z92.string().datetime().optional(),
|
|
4615
|
+
/** Override auto-detection. Rarely needed; detection handles the three formats. */
|
|
4616
|
+
format: z92.enum(["text", "vtt", "srt"]).optional(),
|
|
4617
|
+
source: z92.enum(["manual", "slack"]).optional()
|
|
4618
|
+
});
|
|
4619
|
+
var GetMeetingRequestSchema = z92.object({
|
|
4620
|
+
projectId: z92.string().cuid(),
|
|
4621
|
+
meetingId: z92.string().cuid()
|
|
4622
|
+
});
|
|
4623
|
+
var UpdateMeetingRequestSchema = z92.object({
|
|
4624
|
+
projectId: z92.string().cuid(),
|
|
4625
|
+
meetingId: z92.string().cuid(),
|
|
4626
|
+
title: z92.string().min(1).max(MEETING_TITLE_MAX).optional(),
|
|
4627
|
+
occurredAt: z92.string().datetime().optional()
|
|
4628
|
+
});
|
|
4629
|
+
var RegenerateMeetingSummaryRequestSchema = z92.object({
|
|
4630
|
+
projectId: z92.string().cuid(),
|
|
4631
|
+
meetingId: z92.string().cuid()
|
|
4632
|
+
});
|
|
4633
|
+
var DeleteMeetingRequestSchema = z92.object({
|
|
4634
|
+
projectId: z92.string().cuid(),
|
|
4635
|
+
meetingId: z92.string().cuid()
|
|
4636
|
+
});
|
|
4637
|
+
var ListMeetingsRequestSchema = z92.object({
|
|
4638
|
+
projectId: z92.string().cuid(),
|
|
4639
|
+
limit: z92.number().int().min(1).max(50).optional(),
|
|
4640
|
+
search: z92.string().max(200).optional()
|
|
4641
|
+
});
|
|
4642
|
+
var ReadMeetingTranscriptRequestSchema = z92.object({
|
|
4643
|
+
projectId: z92.string().cuid(),
|
|
4644
|
+
meetingId: z92.string().cuid(),
|
|
4645
|
+
offset: z92.number().int().min(0).optional(),
|
|
4646
|
+
limit: z92.number().int().min(1).max(500).optional()
|
|
4647
|
+
});
|
|
4648
|
+
var TASK_CHAT_HISTORY_LIMIT = 20;
|
|
4649
|
+
var PM_CHAT_HISTORY_LIMIT = 40;
|
|
4650
|
+
var AGENT_CHAT_HISTORY_FETCH_LIMIT = Math.max(TASK_CHAT_HISTORY_LIMIT, PM_CHAT_HISTORY_LIMIT) + 10;
|
|
4651
|
+
function formatModelId(provider, model) {
|
|
4652
|
+
return `${provider}/${model}`;
|
|
2488
4653
|
}
|
|
2489
|
-
function
|
|
2490
|
-
|
|
2491
|
-
|
|
2492
|
-
|
|
2493
|
-
|
|
4654
|
+
function anthropicEntry(model, label, inputPerMillion, outputPerMillion, opts = {}) {
|
|
4655
|
+
return {
|
|
4656
|
+
provider: "anthropic",
|
|
4657
|
+
model,
|
|
4658
|
+
id: formatModelId("anthropic", model),
|
|
4659
|
+
label,
|
|
4660
|
+
format: "anthropic-messages",
|
|
4661
|
+
inputPrice: inputPerMillion / 1e6,
|
|
4662
|
+
outputPrice: outputPerMillion / 1e6,
|
|
4663
|
+
supportsTools: true,
|
|
4664
|
+
supportsEffort: opts.supportsEffort ?? true,
|
|
4665
|
+
...opts.experimental ? { experimental: true } : {}
|
|
4666
|
+
};
|
|
2494
4667
|
}
|
|
2495
|
-
|
|
2496
|
-
|
|
2497
|
-
|
|
2498
|
-
|
|
2499
|
-
|
|
2500
|
-
|
|
2501
|
-
|
|
2502
|
-
|
|
2503
|
-
|
|
2504
|
-
|
|
2505
|
-
|
|
2506
|
-
|
|
2507
|
-
|
|
2508
|
-
|
|
2509
|
-
|
|
2510
|
-
|
|
2511
|
-
|
|
2512
|
-
|
|
2513
|
-
|
|
2514
|
-
|
|
2515
|
-
|
|
2516
|
-
|
|
2517
|
-
|
|
2518
|
-
|
|
2519
|
-
|
|
2520
|
-
|
|
2521
|
-
|
|
2522
|
-
|
|
2523
|
-
|
|
2524
|
-
|
|
2525
|
-
|
|
4668
|
+
var ANTHROPIC_CATALOG = [
|
|
4669
|
+
anthropicEntry(DEFAULT_OPUS_MODEL, "Opus 5 Latest", 5, 25),
|
|
4670
|
+
anthropicEntry(PREVIOUS_OPUS_MODEL, "Opus 4.8", 5, 25),
|
|
4671
|
+
anthropicEntry(DEFAULT_SONNET_MODEL, "Sonnet 5 Latest", 3, 15),
|
|
4672
|
+
anthropicEntry(PREVIOUS_SONNET_MODEL, "Sonnet 4.6", 3, 15),
|
|
4673
|
+
// The Haiku line (4.5 and older) predates the tuning surface and 400s on it.
|
|
4674
|
+
anthropicEntry(DEFAULT_HAIKU_MODEL, "Haiku 4.5", 1, 5, { supportsEffort: false }),
|
|
4675
|
+
anthropicEntry(FABLE_MODEL, "Fable 5 (experimental)", 10, 50, { experimental: true })
|
|
4676
|
+
];
|
|
4677
|
+
var MS_PER_DAY = 24 * 60 * 60 * 1e3;
|
|
4678
|
+
var POSTGRES_ENV = {
|
|
4679
|
+
POSTGRES_HOST_AUTH_METHOD: "trust",
|
|
4680
|
+
POSTGRES_DB: "conveyor"
|
|
4681
|
+
};
|
|
4682
|
+
var FIREBASE_EMULATOR_COMMAND = [
|
|
4683
|
+
"sh",
|
|
4684
|
+
"-c",
|
|
4685
|
+
`set -e; mkdir -p /home/node && cd /home/node && cat > firebase.json <<'EOF'
|
|
4686
|
+
{"emulators":{"auth":{"host":"0.0.0.0","port":9099},"hub":{"host":"0.0.0.0","port":4400},"ui":{"enabled":false}}}
|
|
4687
|
+
EOF
|
|
4688
|
+
exec firebase emulators:start --only=auth --project=rally-cry-dev`
|
|
4689
|
+
];
|
|
4690
|
+
var FIREBASE_EMULATOR_ENV = {
|
|
4691
|
+
METADATA_SERVER_DETECTION: "none",
|
|
4692
|
+
GOOGLE_APPLICATION_CREDENTIALS: "/dev/null"
|
|
4693
|
+
};
|
|
4694
|
+
var CATALOG = {
|
|
4695
|
+
postgresql: {
|
|
4696
|
+
name: "postgresql",
|
|
4697
|
+
image: "postgres:16-alpine",
|
|
4698
|
+
command: [
|
|
4699
|
+
"sh",
|
|
4700
|
+
"-c",
|
|
4701
|
+
// Start postgres, wait for readiness, then create the test database.
|
|
4702
|
+
// Durability flags: service state is ephemeral by design (overlayfs, no
|
|
4703
|
+
// PVC; a crash re-seeds from pod-data), so commits must never wait on a
|
|
4704
|
+
// WAL flush. fsync=off + synchronous_commit=off + full_page_writes=off
|
|
4705
|
+
// remove all blocking storage I/O — without them, per-commit flushes on
|
|
4706
|
+
// the node's network boot disk dominated int-test wall time (~100ms/commit).
|
|
4707
|
+
"if [ ! -s /var/lib/postgresql/data/PG_VERSION ] && [ -d /var/lib/postgresql/pod-data ]; then cp -a /var/lib/postgresql/pod-data/. /var/lib/postgresql/data/; fi; chown -R postgres:postgres /var/lib/postgresql/data; docker-entrypoint.sh postgres -c fsync=off -c synchronous_commit=off -c full_page_writes=off & PID=$!; for i in $(seq 1 30); do pg_isready -U postgres && break; sleep 1; done; createdb -U postgres conveyor_test 2>/dev/null || true; wait $PID"
|
|
4708
|
+
],
|
|
4709
|
+
ports: [5432],
|
|
4710
|
+
livenessProbe: {
|
|
4711
|
+
exec: ["pg_isready", "-U", "postgres"],
|
|
4712
|
+
periodSeconds: 30,
|
|
4713
|
+
failureThreshold: 3,
|
|
4714
|
+
timeoutSeconds: 5,
|
|
4715
|
+
initialDelaySeconds: 60
|
|
4716
|
+
},
|
|
4717
|
+
env: { ...POSTGRES_ENV },
|
|
4718
|
+
resources: {
|
|
4719
|
+
// postgres runs from the baked seed at /var/lib/postgresql/pod-data via
|
|
4720
|
+
// overlayfs CoW (no emptyDir — see k8s-pod-spec.ts), so WAL + catalog +
|
|
4721
|
+
// re-seed churn charges the container's ephemeral-storage. It must exceed
|
|
4722
|
+
// the ~1Gi ceiling where pods evicted, but is bounded by TWO Autopilot rules:
|
|
4723
|
+
// 1) limit == request — Autopilot caps the limit DOWN to the request at
|
|
4724
|
+
// admission, so headroom must live on the REQUEST, not just the limit;
|
|
4725
|
+
// 2) the SUM of all container ephemeral requests in a pod must be ≤ 10Gi
|
|
4726
|
+
// (an emptyDir doesn't escape this: its usage still evicts against the
|
|
4727
|
+
// container limit, and raising the limit re-hits rule 1 → the cap).
|
|
4728
|
+
// The agent is trimmed to 4Gi (resource-tiers.ts) to free room: with
|
|
4729
|
+
// agent 4 + gcsfuse 1 + lgtm 1 + es/redis/firebase 0.75, postgres gets 3Gi
|
|
4730
|
+
// and the heaviest (URC) pod sits at 9.75Gi + 10Mi for GCS Fuse metadata
|
|
4731
|
+
// prefetch — under the 10Gi cap — while giving postgres 3x the ~1Gi
|
|
4732
|
+
// ceiling that evicted. Keep request == limit;
|
|
4733
|
+
// see the per-pod ephemeral budget guard test.
|
|
4734
|
+
// CPU: the request is the CFS floor; the old 50m starved postgres to 5ms
|
|
4735
|
+
// of CPU per 100ms period — every query burst hit throttle stalls, which
|
|
4736
|
+
// showed up as ~100ms floors on trivial statements and dominated the API
|
|
4737
|
+
// int suite even after the fsync flags above. 500m is paid for out of the
|
|
4738
|
+
// agent's derived share (resource-tiers.ts). The limit bursts to 2 for
|
|
4739
|
+
// spiky work — the first-boot pod-data seed copy and int-suite query
|
|
4740
|
+
// storms — borrowing idle node CPU without moving the request.
|
|
4741
|
+
requests: { cpuMillicores: 500, memoryMi: 512, ephemeralMi: 3 * 1024 },
|
|
4742
|
+
limits: { cpuMillicores: 2e3, memoryMi: 512, ephemeralMi: 3 * 1024 }
|
|
4743
|
+
},
|
|
4744
|
+
connectionEnv: {
|
|
4745
|
+
DATABASE_URL: "postgresql://postgres@postgresql:5432/conveyor",
|
|
4746
|
+
TEST_DATABASE_URL: "postgresql://postgres@postgresql:5432/conveyor_test"
|
|
4747
|
+
},
|
|
4748
|
+
statefulBake: true,
|
|
4749
|
+
bake: {
|
|
4750
|
+
// Start postgres, wait for readiness, then create the test database so
|
|
4751
|
+
// it's baked into the committed image. No pod-data restore (nothing is
|
|
4752
|
+
// seeded yet) and no durability flags (the bake's writes must land).
|
|
4753
|
+
// NOTE: docker-compose interpolates `$VAR`/`$(...)`, so every `$` that
|
|
4754
|
+
// must reach the shell is doubled.
|
|
4755
|
+
command: [
|
|
4756
|
+
"sh",
|
|
4757
|
+
"-c",
|
|
4758
|
+
"docker-entrypoint.sh postgres & PID=$$!; for i in $$(seq 1 30); do pg_isready -U postgres && break; sleep 1; done; createdb -U postgres conveyor_test 2>/dev/null || true; wait $$PID"
|
|
4759
|
+
],
|
|
4760
|
+
environment: { ...POSTGRES_ENV },
|
|
4761
|
+
healthcheck: {
|
|
4762
|
+
test: ["CMD-SHELL", "pg_isready -U postgres"],
|
|
4763
|
+
intervalSec: 2,
|
|
4764
|
+
timeoutSec: 5,
|
|
4765
|
+
retries: 15
|
|
4766
|
+
}
|
|
4767
|
+
}
|
|
4768
|
+
},
|
|
4769
|
+
redis: {
|
|
4770
|
+
name: "redis",
|
|
4771
|
+
image: "redis:7-alpine",
|
|
4772
|
+
command: ["redis-server", "--appendonly", "yes", "--dir", "/data"],
|
|
4773
|
+
ports: [6379],
|
|
4774
|
+
env: {},
|
|
4775
|
+
resources: {
|
|
4776
|
+
requests: { cpuMillicores: 25, memoryMi: 32, ephemeralMi: 256 },
|
|
4777
|
+
limits: { cpuMillicores: 50, memoryMi: 64, ephemeralMi: 256 }
|
|
4778
|
+
},
|
|
4779
|
+
connectionEnv: {
|
|
4780
|
+
REDIS_URL: "redis://redis:6379",
|
|
4781
|
+
AUTH_REDIS_URL: "redis://redis:6379"
|
|
4782
|
+
},
|
|
4783
|
+
statefulBake: false,
|
|
4784
|
+
// Redis holds no baked state, so the bake runs the stock image CMD.
|
|
4785
|
+
bake: {},
|
|
4786
|
+
mirror: { src: "redis:7-alpine", dest: "mirror-redis:7-alpine" }
|
|
4787
|
+
},
|
|
4788
|
+
elasticsearch: {
|
|
4789
|
+
name: "elasticsearch",
|
|
4790
|
+
// 9.4.0 matches universal-rally-cry's local compose + its v9 ES client —
|
|
4791
|
+
// the v9 client sends Accept: compatible-with=9, which an 8.x server
|
|
4792
|
+
// rejects (media_type_header_exception), breaking search/audit indexing
|
|
4793
|
+
// in pods. 512m heap matches the project compose sizing.
|
|
4794
|
+
image: "docker.elastic.co/elasticsearch/elasticsearch:9.4.0",
|
|
4795
|
+
ports: [9200],
|
|
4796
|
+
// Baked service images are `docker commit`s of a recently-running ES, so
|
|
4797
|
+
// they carry a stale data-dir node.lock; ES 9 hard-fails on it at boot
|
|
4798
|
+
// ("Underlying file changed by an external force" → AlreadyClosedException).
|
|
4799
|
+
// Clear it before handing off to the stock entrypoint.
|
|
4800
|
+
command: [
|
|
4801
|
+
"sh",
|
|
4802
|
+
"-c",
|
|
4803
|
+
// ALL Lucene lock files, not just node.lock — the baked image also
|
|
4804
|
+
// carries per-index write.lock + snapshot_cache/write.lock, and ES 9
|
|
4805
|
+
// fail-fasts on any of them ("changed by an external force").
|
|
4806
|
+
"find /usr/share/elasticsearch/data -name '*.lock' -type f -delete 2>/dev/null; exec /usr/local/bin/docker-entrypoint.sh eswrapper"
|
|
4807
|
+
],
|
|
4808
|
+
env: {
|
|
4809
|
+
"discovery.type": "single-node",
|
|
4810
|
+
"xpack.security.enabled": "false",
|
|
4811
|
+
"xpack.ml.enabled": "false",
|
|
4812
|
+
"xpack.watcher.enabled": "false",
|
|
4813
|
+
"xpack.profiling.enabled": "false",
|
|
4814
|
+
"ingest.geoip.downloader.enabled": "false",
|
|
4815
|
+
ES_JAVA_OPTS: "-Xms512m -Xmx512m"
|
|
4816
|
+
},
|
|
4817
|
+
resources: {
|
|
4818
|
+
// CPU limit 4x the request: ES cold-start is a CPU-bound JVM boot
|
|
4819
|
+
// (class loading + JIT + recovery of the docker-commit'ed data dir),
|
|
4820
|
+
// and the 500m hard cap put it at ~135s to yellow — past the sidecar
|
|
4821
|
+
// wait script's original 90s budget. Bursting to 2 cut it to ~40s on
|
|
4822
|
+
// the real cluster (A/B on identical nodes, 2 rounds). The burst only
|
|
4823
|
+
// borrows idle node CPU at boot; under contention CFS still floors ES
|
|
4824
|
+
// at its 500m request.
|
|
4825
|
+
requests: { cpuMillicores: 500, memoryMi: 1024, ephemeralMi: 256 },
|
|
4826
|
+
limits: { cpuMillicores: 2e3, memoryMi: 1024, ephemeralMi: 256 }
|
|
4827
|
+
},
|
|
4828
|
+
connectionEnv: {
|
|
4829
|
+
ELASTICSEARCH_URL: "http://elasticsearch:9200"
|
|
4830
|
+
},
|
|
4831
|
+
statefulBake: true,
|
|
4832
|
+
bake: {
|
|
4833
|
+
// No lock-clearing command at bake time: the bake starts from the stock
|
|
4834
|
+
// image, which has no committed data dir to unlock yet.
|
|
4835
|
+
environment: {
|
|
4836
|
+
"discovery.type": "single-node",
|
|
4837
|
+
"xpack.security.enabled": "false",
|
|
4838
|
+
ES_JAVA_OPTS: "-Xms512m -Xmx512m"
|
|
4839
|
+
}
|
|
4840
|
+
}
|
|
4841
|
+
},
|
|
4842
|
+
// All-in-one image bundling Grafana + Loki + Tempo + Mimir + an OTEL Collector.
|
|
4843
|
+
// Pinned tag (not :latest) so the image-builder content hash stays
|
|
4844
|
+
// deterministic across upstream releases — see image-builder.ts content-hash
|
|
4845
|
+
// dedup keyed on `deps.sorted()`.
|
|
4846
|
+
lgtm: {
|
|
4847
|
+
name: "lgtm",
|
|
4848
|
+
image: "grafana/otel-lgtm:0.11.6",
|
|
4849
|
+
// The otel-lgtm image's own CMD is ["/otel-lgtm/run-all.sh"] (WORKDIR
|
|
4850
|
+
// /otel-lgtm). Declaring it explicitly makes lgtm a command-based service so
|
|
4851
|
+
// the lazy start-file gate wraps it like the others — otherwise it launches
|
|
4852
|
+
// via image CMD at boot and can't be parked (which is why warm-booting it at
|
|
4853
|
+
// a minimal request OOMKilled it). getSidecarSpecs strips this command for a
|
|
4854
|
+
// baked lgtm image, so it only applies to the stock image whose launcher is
|
|
4855
|
+
// exactly this path.
|
|
4856
|
+
command: ["/otel-lgtm/run-all.sh"],
|
|
4857
|
+
ports: [
|
|
4858
|
+
// OTLP gRPC + HTTP — agents and user code emit telemetry here.
|
|
4859
|
+
4317,
|
|
4860
|
+
4318,
|
|
4861
|
+
// Grafana UI — reachable through the existing preview proxy on
|
|
4862
|
+
// https://3000-{sessionId}.preview.<PREVIEW_DOMAIN>/.
|
|
4863
|
+
3e3
|
|
4864
|
+
],
|
|
4865
|
+
env: {
|
|
4866
|
+
// The pod is per-task; the preview proxy authenticates the session
|
|
4867
|
+
// upstream, so anonymous in-pod admin is acceptable here.
|
|
4868
|
+
GF_AUTH_ANONYMOUS_ENABLED: "true",
|
|
4869
|
+
GF_AUTH_ANONYMOUS_ORG_ROLE: "Admin",
|
|
4870
|
+
GF_SECURITY_ALLOW_EMBEDDING: "true",
|
|
4871
|
+
ENABLE_LOGS_GRAFANA: "true",
|
|
4872
|
+
ENABLE_LOGS_OTELCOL: "true"
|
|
4873
|
+
},
|
|
4874
|
+
resources: {
|
|
4875
|
+
// Autopilot caps the ephemeral-storage limit to the request (see the
|
|
4876
|
+
// postgresql note), so the 1Gi headroom must be on the request too.
|
|
4877
|
+
requests: { cpuMillicores: 250, memoryMi: 1024, ephemeralMi: 1024 },
|
|
4878
|
+
limits: { cpuMillicores: 1e3, memoryMi: 2048, ephemeralMi: 1024 }
|
|
4879
|
+
},
|
|
4880
|
+
statefulBake: false,
|
|
4881
|
+
// No `bake` block: a BAKE produces traces nobody reads, so starting the
|
|
4882
|
+
// (heavy) collector image would only add a pull + boot to every build.
|
|
4883
|
+
// lgtm still runs for live pods, and is never committed regardless.
|
|
4884
|
+
mirror: { src: "grafana/otel-lgtm:0.11.6", dest: "mirror-otel-lgtm:0.11.6" }
|
|
4885
|
+
},
|
|
4886
|
+
"firebase-auth-emulator": {
|
|
4887
|
+
name: "firebase-auth-emulator",
|
|
4888
|
+
image: "andreysenov/firebase-tools:latest",
|
|
4889
|
+
command: FIREBASE_EMULATOR_COMMAND,
|
|
4890
|
+
ports: [9099, 4400],
|
|
4891
|
+
env: {
|
|
4892
|
+
// The emulator container inherits the pod's Workload Identity, so
|
|
4893
|
+
// firebase-tools finds GCP credentials and its `emulators:start` does an
|
|
4894
|
+
// online "auto auth" + project validation that stalls ~47s on the pod's
|
|
4895
|
+
// locked-down egress (the dominant service boot cost — measured live).
|
|
4896
|
+
// The emulator needs NO real credentials, so cut off credential discovery
|
|
4897
|
+
// for this container only: google-auth-library skips metadata detection
|
|
4898
|
+
// and finds no key file, firebase-tools logs "not authenticated" and
|
|
4899
|
+
// starts the emulator immediately. The agent container keeps its WI.
|
|
4900
|
+
...FIREBASE_EMULATOR_ENV
|
|
4901
|
+
},
|
|
4902
|
+
resources: {
|
|
4903
|
+
requests: { cpuMillicores: 100, memoryMi: 256, ephemeralMi: 256 },
|
|
4904
|
+
limits: { cpuMillicores: 500, memoryMi: 512, ephemeralMi: 256 }
|
|
4905
|
+
},
|
|
4906
|
+
connectionEnv: {
|
|
4907
|
+
// In docker-compose, services reach each other by service name — the
|
|
4908
|
+
// runtime pod equivalents use `localhost` because co-located services
|
|
4909
|
+
// share the pod's network namespace.
|
|
4910
|
+
FIREBASE_AUTH_EMULATOR_HOST: "firebase-auth-emulator:9099",
|
|
4911
|
+
NEXT_PUBLIC_FIREBASE_AUTH_EMULATOR_HOST: "firebase-auth-emulator:9099"
|
|
4912
|
+
},
|
|
4913
|
+
statefulBake: false,
|
|
4914
|
+
mirror: {
|
|
4915
|
+
src: "andreysenov/firebase-tools:latest",
|
|
4916
|
+
dest: "mirror-firebase-tools:latest"
|
|
4917
|
+
},
|
|
4918
|
+
bake: {
|
|
4919
|
+
// The emulator writes its config at startup, so the bake needs the same
|
|
4920
|
+
// inline command the runtime uses.
|
|
4921
|
+
command: FIREBASE_EMULATOR_COMMAND,
|
|
4922
|
+
environment: FIREBASE_EMULATOR_ENV,
|
|
4923
|
+
healthcheck: {
|
|
4924
|
+
// Probe with node, the one runtime this image guarantees. The image
|
|
4925
|
+
// (`andreysenov/firebase-tools`) is a slim Node image that ships
|
|
4926
|
+
// NEITHER `wget` NOR `curl`, so the `wget -qO- …` probe this replaced
|
|
4927
|
+
// exited 127 on every attempt: a healthy emulator burned all 20 retries
|
|
4928
|
+
// and every bake reported it unhealthy. See the catalog invariant in
|
|
4929
|
+
// `service-definitions.test.ts`, which also executes this probe.
|
|
4930
|
+
test: [
|
|
4931
|
+
"CMD-SHELL",
|
|
4932
|
+
`node -e "fetch('http://localhost:9099/').then((response) => process.exit(response.ok ? 0 : 1)).catch(() => process.exit(1))"`
|
|
4933
|
+
],
|
|
4934
|
+
intervalSec: 3,
|
|
4935
|
+
timeoutSec: 5,
|
|
4936
|
+
retries: 20
|
|
4937
|
+
}
|
|
4938
|
+
}
|
|
2526
4939
|
}
|
|
2527
|
-
|
|
2528
|
-
|
|
2529
|
-
|
|
2530
|
-
|
|
2531
|
-
|
|
2532
|
-
|
|
2533
|
-
|
|
2534
|
-
|
|
2535
|
-
|
|
2536
|
-
|
|
2537
|
-
|
|
2538
|
-
|
|
2539
|
-
|
|
2540
|
-
|
|
4940
|
+
};
|
|
4941
|
+
var SERVICE_DEFINITIONS = CATALOG;
|
|
4942
|
+
var SUPPORTED_CLAUDESPACE_DEPS = Object.keys(CATALOG);
|
|
4943
|
+
var STATEFUL_BAKE_DEPS = new Set(
|
|
4944
|
+
SUPPORTED_CLAUDESPACE_DEPS.filter((dep) => SERVICE_DEFINITIONS[dep].statefulBake)
|
|
4945
|
+
);
|
|
4946
|
+
var MIRRORABLE_SIDECARS = Object.fromEntries(
|
|
4947
|
+
SUPPORTED_CLAUDESPACE_DEPS.flatMap((dep) => {
|
|
4948
|
+
const mirror = SERVICE_DEFINITIONS[dep].mirror;
|
|
4949
|
+
return mirror ? [[dep, mirror]] : [];
|
|
4950
|
+
})
|
|
4951
|
+
);
|
|
4952
|
+
var LEVELS_PER_BAND = 100;
|
|
4953
|
+
var PRESTIGE_TIERS = ACHIEVEMENT_RARITIES.map((rarity, index) => ({
|
|
4954
|
+
prestige: index + 1,
|
|
4955
|
+
name: rarity.name,
|
|
4956
|
+
color: rarity.color,
|
|
4957
|
+
iconPath: rarity.iconPath,
|
|
4958
|
+
minLevel: (index + 1) * LEVELS_PER_BAND
|
|
4959
|
+
}));
|
|
4960
|
+
var TOP_PRESTIGE_BAND = PRESTIGE_TIERS.length;
|
|
4961
|
+
var CARD_TYPE_SURFACE = {
|
|
4962
|
+
task: "board",
|
|
4963
|
+
chat: "board",
|
|
4964
|
+
incident: "report",
|
|
4965
|
+
suggestion: "report"
|
|
4966
|
+
};
|
|
4967
|
+
var surfaceTypes = (surface) => Object.keys(CARD_TYPE_SURFACE).filter(
|
|
4968
|
+
(type) => CARD_TYPE_SURFACE[type] === surface
|
|
4969
|
+
);
|
|
4970
|
+
var BOARD_CARD_TYPES = surfaceTypes("board");
|
|
4971
|
+
var REPORT_CARD_TYPES = surfaceTypes("report");
|
|
4972
|
+
|
|
4973
|
+
// src/tools/logs.ts
|
|
4974
|
+
function registerLogTools(server2, conn2) {
|
|
4975
|
+
registerContractTool(server2, queryGcpLogsContract, async (params) => {
|
|
4976
|
+
const text = await runQueryGcpLogs(conn2, params);
|
|
4977
|
+
return { content: [{ type: "text", text }] };
|
|
4978
|
+
});
|
|
4979
|
+
registerContractTool(server2, queryGrafanaLogsContract, async (params) => {
|
|
4980
|
+
const text = await runQueryGrafanaLogs(conn2, params);
|
|
4981
|
+
return { content: [{ type: "text", text }] };
|
|
2541
4982
|
});
|
|
2542
|
-
if (result.error) return result.error;
|
|
2543
|
-
const header = [
|
|
2544
|
-
`env=${params.env ?? "prod"}`,
|
|
2545
|
-
`window=${startTime}\u2192${params.endTime ?? "now"}`,
|
|
2546
|
-
...params.level ? [`minLevel=${params.level}`] : [],
|
|
2547
|
-
...result.logql ? [`logql=${truncateLine(result.logql)}`] : [],
|
|
2548
|
-
`entries=${result.entries.length}`
|
|
2549
|
-
].join(" ");
|
|
2550
|
-
const lines = result.entries.map(formatLogEntryLine);
|
|
2551
|
-
const oldest = result.entries.map((e) => e.timestamp).sort()[0];
|
|
2552
|
-
const footer = result.hasMore ? [`-- hit the limit: older lines exist \u2014 pass endTime="${oldest}" to page further back`] : [];
|
|
2553
|
-
if (lines.length === 0) {
|
|
2554
|
-
return [
|
|
2555
|
-
header,
|
|
2556
|
-
"(no matching log entries \u2014 widen the window, lower minLevel, or drop filters)"
|
|
2557
|
-
].join("\n");
|
|
2558
|
-
}
|
|
2559
|
-
return [header, ...lines, ...footer].join("\n");
|
|
2560
4983
|
}
|
|
2561
|
-
|
|
2562
|
-
|
|
2563
|
-
|
|
2564
|
-
|
|
2565
|
-
|
|
2566
|
-
|
|
2567
|
-
|
|
2568
|
-
|
|
2569
|
-
|
|
2570
|
-
|
|
2571
|
-
|
|
2572
|
-
|
|
2573
|
-
|
|
2574
|
-
|
|
2575
|
-
|
|
2576
|
-
|
|
2577
|
-
|
|
2578
|
-
|
|
2579
|
-
async (params) => {
|
|
2580
|
-
const text = await runQueryGrafanaLogs(conn2, params);
|
|
2581
|
-
return { content: [{ type: "text", text }] };
|
|
4984
|
+
|
|
4985
|
+
// src/tools/integrations.ts
|
|
4986
|
+
function registerIntegrationTools(server2, conn2) {
|
|
4987
|
+
registerContractTool(server2, listProjectIntegrationsContract, async (params) => {
|
|
4988
|
+
const result = await conn2.listProjectIntegrations(params.projectId);
|
|
4989
|
+
return { content: [{ type: "text", text: JSON.stringify(result, null, 2) }] };
|
|
4990
|
+
});
|
|
4991
|
+
registerContractTool(server2, listProjectChannelsContract, async (params) => {
|
|
4992
|
+
const channels = await conn2.listProjectChannels(params.projectId);
|
|
4993
|
+
if (channels.length === 0) {
|
|
4994
|
+
return {
|
|
4995
|
+
content: [
|
|
4996
|
+
{
|
|
4997
|
+
type: "text",
|
|
4998
|
+
text: "No work channels are registered for this project. An admin registers them in project settings; until then no channel is reachable, whatever the bot can see in the workspace."
|
|
4999
|
+
}
|
|
5000
|
+
]
|
|
5001
|
+
};
|
|
2582
5002
|
}
|
|
2583
|
-
|
|
5003
|
+
return { content: [{ type: "text", text: JSON.stringify(channels, null, 2) }] };
|
|
5004
|
+
});
|
|
5005
|
+
registerContractTool(server2, readChannelMessagesContract, async (params) => {
|
|
5006
|
+
const result = await conn2.readChannelMessages(params);
|
|
5007
|
+
return { content: [{ type: "text", text: JSON.stringify(result, null, 2) }] };
|
|
5008
|
+
});
|
|
5009
|
+
registerContractTool(server2, getAnalyticsSummaryContract, async (params) => {
|
|
5010
|
+
const res = await conn2.getProjectAnalyticsSummary(params);
|
|
5011
|
+
if (!res.configured) {
|
|
5012
|
+
return {
|
|
5013
|
+
content: [
|
|
5014
|
+
{
|
|
5015
|
+
type: "text",
|
|
5016
|
+
text: res.message ?? "Google Analytics is not configured for this project."
|
|
5017
|
+
}
|
|
5018
|
+
]
|
|
5019
|
+
};
|
|
5020
|
+
}
|
|
5021
|
+
return { content: [{ type: "text", text: JSON.stringify(res, null, 2) }] };
|
|
5022
|
+
});
|
|
5023
|
+
registerContractTool(server2, postChannelMessageContract, async (params) => {
|
|
5024
|
+
const result = await conn2.postChannelMessage(params);
|
|
5025
|
+
return {
|
|
5026
|
+
content: [
|
|
5027
|
+
{ type: "text", text: `Posted to ${result.channelId} (message ${result.messageId}).` }
|
|
5028
|
+
]
|
|
5029
|
+
};
|
|
5030
|
+
});
|
|
2584
5031
|
}
|
|
2585
|
-
|
|
2586
|
-
|
|
2587
|
-
|
|
2588
|
-
|
|
2589
|
-
|
|
2590
|
-
|
|
2591
|
-
|
|
2592
|
-
|
|
2593
|
-
|
|
2594
|
-
|
|
2595
|
-
|
|
2596
|
-
|
|
2597
|
-
|
|
2598
|
-
|
|
2599
|
-
|
|
2600
|
-
|
|
2601
|
-
|
|
2602
|
-
|
|
2603
|
-
|
|
2604
|
-
|
|
2605
|
-
|
|
2606
|
-
|
|
2607
|
-
|
|
2608
|
-
|
|
2609
|
-
|
|
2610
|
-
|
|
2611
|
-
|
|
2612
|
-
|
|
5032
|
+
|
|
5033
|
+
// src/tools/drive.ts
|
|
5034
|
+
var MAX_READ_CHARS = 1e5;
|
|
5035
|
+
function registerDriveTools(server2, conn2) {
|
|
5036
|
+
registerContractTool(server2, driveListFilesContract, async (params) => {
|
|
5037
|
+
const result = await conn2.listProjectDriveFiles(params);
|
|
5038
|
+
return { content: [{ type: "text", text: JSON.stringify(result, null, 2) }] };
|
|
5039
|
+
});
|
|
5040
|
+
registerContractTool(server2, driveReadFileContract, async (params) => {
|
|
5041
|
+
const result = await conn2.readProjectDriveFile(params);
|
|
5042
|
+
const overCap = result.content.length > MAX_READ_CHARS;
|
|
5043
|
+
const content = overCap ? result.content.slice(0, MAX_READ_CHARS) : result.content;
|
|
5044
|
+
const notes = [
|
|
5045
|
+
result.exported ? "(exported from a Google-native document)" : null,
|
|
5046
|
+
result.truncated || overCap ? "(truncated at the 100 KB read limit)" : null
|
|
5047
|
+
].filter(Boolean);
|
|
5048
|
+
const header = `${result.file.name} ${notes.join(" ")}`.trim();
|
|
5049
|
+
return { content: [{ type: "text", text: `${header}
|
|
5050
|
+
|
|
5051
|
+
${content}` }] };
|
|
5052
|
+
});
|
|
5053
|
+
registerContractTool(server2, driveCreateFileContract, async (params) => {
|
|
5054
|
+
const file = await conn2.createProjectDriveFile(params);
|
|
5055
|
+
return { content: [{ type: "text", text: `Created "${file.name}" (${file.id})` }] };
|
|
5056
|
+
});
|
|
5057
|
+
registerContractTool(server2, driveUpdateFileContract, async (params) => {
|
|
5058
|
+
const file = await conn2.updateProjectDriveFile(params);
|
|
5059
|
+
return { content: [{ type: "text", text: `Updated "${file.name}" (${file.id})` }] };
|
|
5060
|
+
});
|
|
5061
|
+
registerContractTool(server2, driveDeleteFileContract, async (params) => {
|
|
5062
|
+
const result = await conn2.deleteProjectDriveFile(params);
|
|
5063
|
+
return {
|
|
5064
|
+
content: [
|
|
5065
|
+
{ type: "text", text: `Moved "${result.name}" (${result.id}) to the Google Drive trash` }
|
|
5066
|
+
]
|
|
5067
|
+
};
|
|
5068
|
+
});
|
|
5069
|
+
registerContractTool(server2, driveCreateFolderContract, async (params) => {
|
|
5070
|
+
const folder = await conn2.createProjectDriveFolder(params);
|
|
5071
|
+
return { content: [{ type: "text", text: `Created folder "${folder.name}" (${folder.id})` }] };
|
|
5072
|
+
});
|
|
5073
|
+
}
|
|
5074
|
+
|
|
5075
|
+
// src/tools/meetings.ts
|
|
5076
|
+
function registerMeetingTools(server2, conn2) {
|
|
5077
|
+
registerContractTool(server2, listMeetingsContract, async (params) => {
|
|
5078
|
+
const res = await conn2.listMeetings(params);
|
|
5079
|
+
if (res.meetings.length === 0) {
|
|
5080
|
+
return {
|
|
5081
|
+
content: [
|
|
5082
|
+
{
|
|
5083
|
+
type: "text",
|
|
5084
|
+
text: params.search ? `No meetings match "${params.search}". Try list_meetings with no search to see what exists.` : "This project has no meetings yet."
|
|
5085
|
+
}
|
|
5086
|
+
]
|
|
5087
|
+
};
|
|
2613
5088
|
}
|
|
2614
|
-
|
|
2615
|
-
|
|
5089
|
+
return { content: [{ type: "text", text: JSON.stringify(res.meetings, null, 2) }] };
|
|
5090
|
+
});
|
|
5091
|
+
registerContractTool(server2, getMeetingContract, async (params) => {
|
|
5092
|
+
const res = await conn2.getMeeting(params);
|
|
5093
|
+
return { content: [{ type: "text", text: JSON.stringify(res, null, 2) }] };
|
|
5094
|
+
});
|
|
5095
|
+
registerContractTool(server2, readMeetingTranscriptContract, async (params) => {
|
|
5096
|
+
const res = await conn2.readMeetingTranscript(params);
|
|
5097
|
+
const header = `${res.title} \u2014 segments ${res.offset + 1}-${res.offset + res.lines.length} of ${res.segmentCount}`;
|
|
5098
|
+
const footer = res.nextOffset === void 0 ? "" : `
|
|
5099
|
+
|
|
5100
|
+
-- more remains: pass offset=${res.nextOffset} to continue`;
|
|
5101
|
+
return { content: [{ type: "text", text: `${header}
|
|
5102
|
+
|
|
5103
|
+
${res.lines.join("\n")}${footer}` }] };
|
|
5104
|
+
});
|
|
2616
5105
|
}
|
|
2617
5106
|
|
|
2618
5107
|
// src/tools/index.ts
|
|
@@ -2630,6 +5119,9 @@ function registerAllTools(server2, conn2) {
|
|
|
2630
5119
|
registerChecklistTools(server2, conn2);
|
|
2631
5120
|
registerWorkspaceTools(server2, conn2);
|
|
2632
5121
|
registerLogTools(server2, conn2);
|
|
5122
|
+
registerIntegrationTools(server2, conn2);
|
|
5123
|
+
registerDriveTools(server2, conn2);
|
|
5124
|
+
registerMeetingTools(server2, conn2);
|
|
2633
5125
|
}
|
|
2634
5126
|
|
|
2635
5127
|
// src/cli.ts
|