@rallycry/conveyor-mcp 5.0.7 → 5.0.9
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-77XLXIFP.js → chunk-UPLS63HM.js} +22 -3
- package/dist/cli.js +1045 -773
- package/dist/{connection-DeBZ_T6f.d.ts → connection-YYBLNxYn.d.ts} +12 -18
- package/dist/index.d.ts +1 -1
- package/dist/index.js +1 -1
- package/dist/tunnel-cli.js +1 -1
- package/dist/tunnel.d.ts +1 -1
- package/dist/wait-cli.js +1 -1
- package/dist/wait-runner.d.ts +1 -1
- package/dist/wait.d.ts +1 -1
- package/package.json +1 -1
package/dist/cli.js
CHANGED
|
@@ -4,7 +4,7 @@ import {
|
|
|
4
4
|
} from "./chunk-5KU3QS6A.js";
|
|
5
5
|
import {
|
|
6
6
|
ConveyorConnection
|
|
7
|
-
} from "./chunk-
|
|
7
|
+
} from "./chunk-UPLS63HM.js";
|
|
8
8
|
|
|
9
9
|
// src/cli.ts
|
|
10
10
|
import { createRequire } from "module";
|
|
@@ -72,8 +72,9 @@ function registerProjectTools(server2, conn2) {
|
|
|
72
72
|
// src/tools/connection.ts
|
|
73
73
|
import { z as z4 } from "zod";
|
|
74
74
|
|
|
75
|
-
// ../shared/dist/chunk-
|
|
75
|
+
// ../shared/dist/chunk-ZSXAYKD4.js
|
|
76
76
|
import { z as z2 } from "zod";
|
|
77
|
+
import { z as z22 } from "zod";
|
|
77
78
|
var DEFAULT_SONNET_MODEL = "claude-sonnet-5";
|
|
78
79
|
var DEFAULT_OPUS_MODEL = "claude-opus-5-5";
|
|
79
80
|
var DEFAULT_HAIKU_MODEL = "claude-haiku-4-5-20251001";
|
|
@@ -146,6 +147,12 @@ var projectCheckpointSettingsSchema = z2.object({
|
|
|
146
147
|
var CARD_DESCRIPTION_MAX = 255;
|
|
147
148
|
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.`;
|
|
148
149
|
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`;
|
|
150
|
+
var MAX_MANUAL_TESTS_PER_TASK = 5;
|
|
151
|
+
var MAX_MANUAL_TEST_TITLE_LENGTH = 200;
|
|
152
|
+
var ManualTestItemsSchema = z22.array(z22.object({ title: z22.string().min(1).max(MAX_MANUAL_TEST_TITLE_LENGTH) })).min(1).max(MAX_MANUAL_TESTS_PER_TASK);
|
|
153
|
+
function describeManualTestRejection(item) {
|
|
154
|
+
return item.reason === "cap" ? `Rejected "${item.title}": the card already holds ${MAX_MANUAL_TESTS_PER_TASK} manual tests. Use remove_manual_test or edit_manual_test to consolidate.` : `Rejected "${item.title}": names an automated step (lint, tests, CI, shell); manual tests describe what a person does in the app.`;
|
|
155
|
+
}
|
|
149
156
|
var DEFAULT_CI_WAIT_TIMEOUT_MINUTES = 45;
|
|
150
157
|
var MAX_CI_WAIT_TIMEOUT_MINUTES = 180;
|
|
151
158
|
var SEVERITY_ENUM = [
|
|
@@ -306,14 +313,14 @@ var f = {
|
|
|
306
313
|
return { kind: "nullable", inner };
|
|
307
314
|
}
|
|
308
315
|
};
|
|
309
|
-
function compileString(
|
|
310
|
-
let schema =
|
|
316
|
+
function compileString(z14, spec) {
|
|
317
|
+
let schema = z14.string();
|
|
311
318
|
if (spec.min !== void 0) schema = schema.min(spec.min);
|
|
312
319
|
if (spec.max !== void 0) schema = schema.max(spec.max);
|
|
313
320
|
return schema;
|
|
314
321
|
}
|
|
315
|
-
function compileNumber(
|
|
316
|
-
let schema =
|
|
322
|
+
function compileNumber(z14, spec) {
|
|
323
|
+
let schema = z14.number();
|
|
317
324
|
if (spec.int) schema = schema.int();
|
|
318
325
|
if (spec.positive) schema = schema.positive();
|
|
319
326
|
if (spec.nonnegative) schema = schema.nonnegative();
|
|
@@ -321,49 +328,50 @@ function compileNumber(z13, spec) {
|
|
|
321
328
|
if (spec.max !== void 0) schema = schema.max(spec.max);
|
|
322
329
|
return schema;
|
|
323
330
|
}
|
|
324
|
-
function compileArray(
|
|
325
|
-
let schema =
|
|
331
|
+
function compileArray(z14, spec) {
|
|
332
|
+
let schema = z14.array(compileField(z14, spec.item));
|
|
326
333
|
if (spec.min !== void 0) schema = schema.min(spec.min);
|
|
334
|
+
if (spec.max !== void 0) schema = schema.max(spec.max);
|
|
327
335
|
return schema;
|
|
328
336
|
}
|
|
329
|
-
function compileBase(
|
|
337
|
+
function compileBase(z14, spec) {
|
|
330
338
|
switch (spec.kind) {
|
|
331
339
|
case "string":
|
|
332
|
-
return compileString(
|
|
340
|
+
return compileString(z14, spec);
|
|
333
341
|
case "number":
|
|
334
|
-
return compileNumber(
|
|
342
|
+
return compileNumber(z14, spec);
|
|
335
343
|
case "boolean":
|
|
336
|
-
return
|
|
344
|
+
return z14.boolean();
|
|
337
345
|
case "enum":
|
|
338
|
-
return
|
|
346
|
+
return z14.enum([...spec.values]);
|
|
339
347
|
case "array":
|
|
340
|
-
return compileArray(
|
|
348
|
+
return compileArray(z14, spec);
|
|
341
349
|
case "object":
|
|
342
|
-
return
|
|
350
|
+
return z14.object(compileShape(z14, spec.fields));
|
|
343
351
|
}
|
|
344
352
|
}
|
|
345
353
|
function descriptionOf(spec) {
|
|
346
354
|
if (spec.kind === "optional" || spec.kind === "nullable") return descriptionOf(spec.inner);
|
|
347
355
|
return spec.desc;
|
|
348
356
|
}
|
|
349
|
-
function compileUndescribed(
|
|
357
|
+
function compileUndescribed(z14, spec) {
|
|
350
358
|
if (spec.kind === "optional") {
|
|
351
|
-
return compileUndescribed(
|
|
359
|
+
return compileUndescribed(z14, spec.inner).optional();
|
|
352
360
|
}
|
|
353
361
|
if (spec.kind === "nullable") {
|
|
354
|
-
return compileUndescribed(
|
|
362
|
+
return compileUndescribed(z14, spec.inner).nullable();
|
|
355
363
|
}
|
|
356
|
-
return compileBase(
|
|
364
|
+
return compileBase(z14, spec);
|
|
357
365
|
}
|
|
358
|
-
function compileField(
|
|
359
|
-
const schema = compileUndescribed(
|
|
366
|
+
function compileField(z14, spec) {
|
|
367
|
+
const schema = compileUndescribed(z14, spec);
|
|
360
368
|
const desc = descriptionOf(spec);
|
|
361
369
|
return desc === void 0 ? schema : schema.describe(desc);
|
|
362
370
|
}
|
|
363
|
-
function compileShape(
|
|
371
|
+
function compileShape(z14, fields) {
|
|
364
372
|
const shape = {};
|
|
365
373
|
for (const [key, spec] of Object.entries(fields)) {
|
|
366
|
-
shape[key] = compileField(
|
|
374
|
+
shape[key] = compileField(z14, spec);
|
|
367
375
|
}
|
|
368
376
|
return shape;
|
|
369
377
|
}
|
|
@@ -453,16 +461,24 @@ var readTaskChatContract = defineToolContract({
|
|
|
453
461
|
}
|
|
454
462
|
}
|
|
455
463
|
});
|
|
464
|
+
var listTagsDetail = f.optional(
|
|
465
|
+
f.enum(["summary", "full"], {
|
|
466
|
+
desc: `"summary" (default) returns one compact row per tag with a contextPathCount. "full" also ships each tag's contextPaths inline (each link with its verification status). Full mode can exceed a client's tool-output budget on a large glossary; use get_tag for one term instead.`
|
|
467
|
+
})
|
|
468
|
+
);
|
|
456
469
|
var listTagsContract = defineToolContract({
|
|
457
470
|
name: "list_tags",
|
|
458
471
|
agent: {
|
|
459
|
-
description: "List the project glossary
|
|
460
|
-
fields: {
|
|
472
|
+
description: "List the whole project glossary in one call. The default summary gives every tag's id, name, color, description, parent/child tag ids, whether it carries a full overview (hasOverview), its attachmentCount (files labelled as examples of the term), and its contextPathCount (the rule/doc/file/folder links it wires into agent context \u2014 `0` means none). Pass detail: \"full\" to ship the contextPaths themselves inline. Use get_tag for one term's full overview and full link verification. Use the ids with get_tag / update_tag.",
|
|
473
|
+
fields: {
|
|
474
|
+
detail: listTagsDetail
|
|
475
|
+
}
|
|
461
476
|
},
|
|
462
477
|
mcp: {
|
|
463
|
-
description:
|
|
478
|
+
description: 'List all project tags in one call. The default summary gives names, IDs, colors, descriptions, hierarchy (parent/child ids), a hasOverview flag, an attachmentCount (files labelled as examples of the term \u2014 read the tiles with list_tag_attachments), and a contextPathCount (the rule/doc/file/folder links each tag wires into agent context \u2014 `0` means none). Pass detail: "full" to ship the contextPaths themselves inline. Call get_tag for one term\'s full overview and full link verification. Pass projectId to target a specific project; otherwise the configured default project is used.',
|
|
464
479
|
fields: {
|
|
465
|
-
projectId: mcpProjectId
|
|
480
|
+
projectId: mcpProjectId,
|
|
481
|
+
detail: listTagsDetail
|
|
466
482
|
}
|
|
467
483
|
}
|
|
468
484
|
});
|
|
@@ -598,7 +614,7 @@ var updateTaskContract = defineToolContract({
|
|
|
598
614
|
}
|
|
599
615
|
},
|
|
600
616
|
mcp: {
|
|
601
|
-
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.",
|
|
617
|
+
description: "Update task fields: title, description, plan, status, risk, story points, assignment, githubBranch, onHold, 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. Set onHold to park a card (true) or bring it back to the active board (false) without cancelling it. 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.",
|
|
602
618
|
fields: {
|
|
603
619
|
projectId: mcpProjectId,
|
|
604
620
|
taskId: f.string({ desc: "The task ID" }),
|
|
@@ -629,6 +645,11 @@ var updateTaskContract = defineToolContract({
|
|
|
629
645
|
)
|
|
630
646
|
),
|
|
631
647
|
githubBranch: f.optional(f.nullable(f.string({ desc: GITHUB_BRANCH_DESC }))),
|
|
648
|
+
onHold: f.optional(
|
|
649
|
+
f.boolean({
|
|
650
|
+
desc: "Park the card on hold (true) or return it to the active board (false). On hold hides the card from the active columns without cancelling it; a card already at an active work status is demoted to Open when parked."
|
|
651
|
+
})
|
|
652
|
+
),
|
|
632
653
|
addTags: f.optional(
|
|
633
654
|
f.array(f.string(), {
|
|
634
655
|
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.'
|
|
@@ -672,9 +693,20 @@ var testStatuses = f.optional(
|
|
|
672
693
|
})
|
|
673
694
|
);
|
|
674
695
|
var setManualTestItems = f.array(
|
|
675
|
-
f.object({
|
|
676
|
-
|
|
696
|
+
f.object({
|
|
697
|
+
title: f.string({
|
|
698
|
+
min: 1,
|
|
699
|
+
max: MAX_MANUAL_TEST_TITLE_LENGTH,
|
|
700
|
+
desc: "One sentence a non-engineer can follow: where to go, what to do, what they should see. No commands, no code."
|
|
701
|
+
})
|
|
702
|
+
}),
|
|
703
|
+
{
|
|
704
|
+
min: 1,
|
|
705
|
+
max: MAX_MANUAL_TESTS_PER_TASK,
|
|
706
|
+
desc: `Manual tests to add. A card holds at most ${MAX_MANUAL_TESTS_PER_TASK}; most changes need 1, and a change with no user-visible behavior needs none.`
|
|
707
|
+
}
|
|
677
708
|
);
|
|
709
|
+
var setManualTestsBar = 'A manual test is a real user path in plain language ("Open a card with a PR: the Pull Request slat sits directly under Description"), not an engineering check. Never record lint, typecheck, unit tests, CI, curl, database, log, or deploy steps; those run automatically. Record at most 5 per card, usually 1, and none when the change has no user-visible behavior. Existing items with the same title are skipped; items that name an automated step or exceed the cap are rejected and reported back.';
|
|
678
710
|
var titleToEdit = f.string({ min: 1, desc: "The current title of the manual test to edit" });
|
|
679
711
|
var newTitle = f.string({ min: 1, desc: "The new title for the manual test" });
|
|
680
712
|
var titleToRemove = f.string({ min: 1, desc: "The title of the manual test to remove" });
|
|
@@ -728,13 +760,13 @@ var queryManualTestsContract = defineToolContract({
|
|
|
728
760
|
var setManualTestsContract = defineToolContract({
|
|
729
761
|
name: "set_manual_tests",
|
|
730
762
|
agent: {
|
|
731
|
-
description:
|
|
763
|
+
description: `Record the manual tests a person should run in the app to sign off on this card. ${setManualTestsBar}`,
|
|
732
764
|
fields: {
|
|
733
765
|
items: setManualTestItems
|
|
734
766
|
}
|
|
735
767
|
},
|
|
736
768
|
mcp: {
|
|
737
|
-
description:
|
|
769
|
+
description: `Record the manual tests a person should run in the app to sign off on a card. Pass projectId to target a specific project; otherwise the configured default project is used. ${setManualTestsBar}`,
|
|
738
770
|
fields: {
|
|
739
771
|
projectId: mcpProjectId,
|
|
740
772
|
taskId: mcpChecklistTaskId,
|
|
@@ -1057,11 +1089,18 @@ var subtasksContracts = [
|
|
|
1057
1089
|
setTaskParentContract
|
|
1058
1090
|
];
|
|
1059
1091
|
var mcpTaskIdOrSlug = f.string({ desc: "The task ID or slug" });
|
|
1092
|
+
var agentTaskIdOrSlug = f.optional(
|
|
1093
|
+
f.string({
|
|
1094
|
+
desc: "Omit for the current card; pass a parent, sibling, or any other card in this project (id or slug) to read its files instead."
|
|
1095
|
+
})
|
|
1096
|
+
);
|
|
1060
1097
|
var listTaskFilesContract = defineToolContract({
|
|
1061
1098
|
name: "list_task_files",
|
|
1062
1099
|
agent: {
|
|
1063
|
-
description: "List all files attached to this task with metadata. Use before fetching a specific file to see what is available and how large each is. For file contents use get_attachment.",
|
|
1064
|
-
fields: {
|
|
1100
|
+
description: "List all files attached to this task with metadata. Use before fetching a specific file to see what is available and how large each is. For file contents use get_attachment. Pass task_id to list another card's files in this project, for example the pack parent's source documents.",
|
|
1101
|
+
fields: {
|
|
1102
|
+
task_id: agentTaskIdOrSlug
|
|
1103
|
+
}
|
|
1065
1104
|
},
|
|
1066
1105
|
mcp: {
|
|
1067
1106
|
description: "List all files attached to a task with metadata (no contents \u2014 fast and small). Pass projectId to target a specific project; otherwise the configured default project is used. Use before fetching a specific file to see what is available and how large each is. For file contents use get_attachment.",
|
|
@@ -1074,13 +1113,14 @@ var listTaskFilesContract = defineToolContract({
|
|
|
1074
1113
|
var getAttachmentContract = defineToolContract({
|
|
1075
1114
|
name: "get_attachment",
|
|
1076
1115
|
agent: {
|
|
1077
|
-
description: "Fetch one task file's content plus metadata by file ID. Call list_task_files first to discover IDs and check sizes \u2014 large binaries may be truncated by the service's size limit.",
|
|
1116
|
+
description: "Fetch one task file's content plus metadata by file ID. This is the way to read attachment bytes: a document `downloadUrl` (anything that is not an image or video) answers 404 without a signed-in project member's session, so do not curl or WebFetch it from the pod. Call list_task_files first to discover IDs and check sizes \u2014 large binaries may be truncated by the service's size limit. Pass task_id when the file is attached to another card in this project, such as the pack parent.",
|
|
1078
1117
|
fields: {
|
|
1079
|
-
fileId: f.string({ desc: "The file ID to retrieve" })
|
|
1118
|
+
fileId: f.string({ desc: "The file ID to retrieve" }),
|
|
1119
|
+
task_id: agentTaskIdOrSlug
|
|
1080
1120
|
}
|
|
1081
1121
|
},
|
|
1082
1122
|
mcp: {
|
|
1083
|
-
description: "Fetch one task file's content plus metadata by file ID (accepts task id or slug). Pass projectId to target a specific project; otherwise the configured default project is used. Images are returned as viewable image blocks. Large text files (logs, JSON) are returned in pages \u2014 use `offset`/`maxBytes` to read more
|
|
1123
|
+
description: "Fetch one task file's content plus metadata by file ID (accepts task id or slug). Pass projectId to target a specific project; otherwise the configured default project is used. Images are returned as viewable image blocks. Large text files (logs, JSON) are returned in pages \u2014 use `offset`/`maxBytes` to read more. This tool is the way to read bytes: a document `downloadUrl` (anything that is not an image or video) answers 404 unless the request carries a signed-in project member's session, so do not curl or fetch it. Call list_task_files first to discover IDs and sizes.",
|
|
1084
1124
|
fields: {
|
|
1085
1125
|
projectId: mcpProjectId,
|
|
1086
1126
|
taskId: mcpTaskIdOrSlug,
|
|
@@ -1147,10 +1187,29 @@ var uploadAttachmentContract = defineToolContract({
|
|
|
1147
1187
|
}
|
|
1148
1188
|
}
|
|
1149
1189
|
});
|
|
1190
|
+
var DELETE_ATTACHMENT_WARNING = "Permanent, with no trash and no undo (unlike drive_delete_file): the file and its bytes are gone, and the chat message that announced it keeps its text but loses the attachment. Use it to remove a superseded upload once its replacement is attached. `fileId` comes from list_task_files.";
|
|
1191
|
+
var deleteAttachmentContract = defineToolContract({
|
|
1192
|
+
name: "delete_attachment",
|
|
1193
|
+
agent: {
|
|
1194
|
+
description: `Delete one attachment from this task. ${DELETE_ATTACHMENT_WARNING} A pod can only delete files on its own card.`,
|
|
1195
|
+
fields: {
|
|
1196
|
+
fileId: f.string({ desc: "The file ID to delete" })
|
|
1197
|
+
}
|
|
1198
|
+
},
|
|
1199
|
+
mcp: {
|
|
1200
|
+
description: `Delete one attachment from a task (accepts task id or slug). ${DELETE_ATTACHMENT_WARNING} Requires Moderate on the project, the same bar as the web Files delete button. Pass projectId to target a specific project; otherwise the configured default project is used.`,
|
|
1201
|
+
fields: {
|
|
1202
|
+
projectId: mcpProjectId,
|
|
1203
|
+
taskId: mcpTaskIdOrSlug,
|
|
1204
|
+
fileId: f.string({ desc: "The file ID to delete" })
|
|
1205
|
+
}
|
|
1206
|
+
}
|
|
1207
|
+
});
|
|
1150
1208
|
var attachmentsContracts = [
|
|
1151
1209
|
listTaskFilesContract,
|
|
1152
1210
|
getAttachmentContract,
|
|
1153
|
-
uploadAttachmentContract
|
|
1211
|
+
uploadAttachmentContract,
|
|
1212
|
+
deleteAttachmentContract
|
|
1154
1213
|
];
|
|
1155
1214
|
var createSuggestionContract = defineToolContract({
|
|
1156
1215
|
name: "create_suggestion",
|
|
@@ -1473,7 +1532,7 @@ var createPullRequestContract = defineToolContract({
|
|
|
1473
1532
|
fields: {
|
|
1474
1533
|
title: f.string({ desc: "The PR title" }),
|
|
1475
1534
|
body: f.string({
|
|
1476
|
-
desc: "The PR description/body in markdown. If the diff changes anything a user sees rendered, the body MUST show the visual proof inline, not just point at the card: upload the capture with upload_attachment, then embed the downloadUrl it returns under a '## Screenshots' heading ( for images, [caption](url) for video). Attach to the card as well \u2014 the card is the durable home, the PR body is what the reviewer reads."
|
|
1535
|
+
desc: "The PR description/body in markdown. If the diff changes anything a user sees rendered, the body MUST show the visual proof inline, not just point at the card: upload the capture with upload_attachment, then embed the downloadUrl it returns under a '## Screenshots' heading ( for images, [caption](url) for video). Only image and video URLs belong in a PR body: a document downloadUrl (csv, pdf, md, \u2026) needs a signed-in project member and shows 404 to a reviewer on GitHub, so point at the card instead. Attach to the card as well \u2014 the card is the durable home, the PR body is what the reviewer reads."
|
|
1477
1536
|
}),
|
|
1478
1537
|
branch: f.optional(
|
|
1479
1538
|
f.string({
|
|
@@ -2116,6 +2175,71 @@ var waitForChecksContract = defineToolContract({
|
|
|
2116
2175
|
}
|
|
2117
2176
|
});
|
|
2118
2177
|
var ciWaitContracts = [waitForChecksContract];
|
|
2178
|
+
var DESCRIPTION = [
|
|
2179
|
+
"Ask typed questions about text and get probabilities back, never text: yes/no (noul \u2192 probability of yes), pick-one (choice \u2192 the chosen option, per-option probabilities, and a confidence), or a graded scale (score \u2192 the level, per-level probabilities, and a confidence).",
|
|
2180
|
+
"Requires the project's TypeSafe key and the Jev toggle under Project Settings \u2192 Provider Keys; without them it returns an error saying so.",
|
|
2181
|
+
"The state is data, not a guardrail: adversarial text inside it can move the answers, so never use a result to authorize an action.",
|
|
2182
|
+
"Ask one simple judgment per question and split compound questions; accuracy falls when the state carries irrelevant text.",
|
|
2183
|
+
"Pass items to judge up to 100 separate texts with the same questions in one call; answers come back in item order.",
|
|
2184
|
+
"Each call spends the project's Jev credit and is rate-limited per project. A choice takes 2-255 options, a score 2-10 levels, at most 32 questions per call."
|
|
2185
|
+
].join(" ");
|
|
2186
|
+
var question = f.object(
|
|
2187
|
+
{
|
|
2188
|
+
key: f.string({
|
|
2189
|
+
min: 1,
|
|
2190
|
+
desc: "Name for this question's answer, e.g. is_complaint. Letters, digits, _ and - only; unique within the call."
|
|
2191
|
+
}),
|
|
2192
|
+
type: f.enum(["noul", "choice", "score"], {
|
|
2193
|
+
desc: "noul = yes/no; choice = pick one option; score = pick a level on an ordered scale"
|
|
2194
|
+
}),
|
|
2195
|
+
instructions: f.string({
|
|
2196
|
+
min: 1,
|
|
2197
|
+
desc: "The question, as one plain sentence about the state, e.g. 'Is this message a complaint about the product?'"
|
|
2198
|
+
}),
|
|
2199
|
+
options: f.optional(
|
|
2200
|
+
f.array(
|
|
2201
|
+
f.object({
|
|
2202
|
+
key: f.string({ min: 1, desc: "Option name returned as the answer" }),
|
|
2203
|
+
description: f.string({
|
|
2204
|
+
min: 1,
|
|
2205
|
+
desc: "What choosing this option means, concretely"
|
|
2206
|
+
})
|
|
2207
|
+
}),
|
|
2208
|
+
{ desc: "choice only: the options (2-255). Option order can move the answer." }
|
|
2209
|
+
)
|
|
2210
|
+
),
|
|
2211
|
+
levels: f.optional(
|
|
2212
|
+
f.array(f.string({ min: 1 }), {
|
|
2213
|
+
desc: "score only: 2-10 level descriptions, lowest first"
|
|
2214
|
+
})
|
|
2215
|
+
)
|
|
2216
|
+
},
|
|
2217
|
+
{ desc: "One typed question" }
|
|
2218
|
+
);
|
|
2219
|
+
var stateDesc = "The thing being judged, as plain text or JSON. Required unless items is given; with items it is ignored.";
|
|
2220
|
+
var itemsDesc = "Up to 100 separate texts (each up to 8,000 characters), each judged on its own with the same questions. Use this for bulk classification instead of one call per item.";
|
|
2221
|
+
var questionsDesc = "1-32 questions, all answered independently against the same state.";
|
|
2222
|
+
var evaluateQuestionsContract = defineToolContract({
|
|
2223
|
+
name: "evaluate_questions",
|
|
2224
|
+
agent: {
|
|
2225
|
+
description: DESCRIPTION,
|
|
2226
|
+
fields: {
|
|
2227
|
+
state: f.optional(f.string({ desc: stateDesc })),
|
|
2228
|
+
questions: f.array(question, { min: 1, desc: questionsDesc }),
|
|
2229
|
+
items: f.optional(f.array(f.string(), { desc: itemsDesc }))
|
|
2230
|
+
}
|
|
2231
|
+
},
|
|
2232
|
+
mcp: {
|
|
2233
|
+
description: `${DESCRIPTION} Pass projectId to target a specific project; otherwise the configured default project is used.`,
|
|
2234
|
+
fields: {
|
|
2235
|
+
projectId: mcpProjectId,
|
|
2236
|
+
state: f.optional(f.string({ desc: stateDesc })),
|
|
2237
|
+
questions: f.array(question, { min: 1, desc: questionsDesc }),
|
|
2238
|
+
items: f.optional(f.array(f.string(), { desc: itemsDesc }))
|
|
2239
|
+
}
|
|
2240
|
+
}
|
|
2241
|
+
});
|
|
2242
|
+
var evaluateContracts = [evaluateQuestionsContract];
|
|
2119
2243
|
var TOOL_CONTRACTS = Object.fromEntries(
|
|
2120
2244
|
[
|
|
2121
2245
|
...tasksContracts,
|
|
@@ -2132,7 +2256,8 @@ var TOOL_CONTRACTS = Object.fromEntries(
|
|
|
2132
2256
|
...driveContracts,
|
|
2133
2257
|
...meetingsContracts,
|
|
2134
2258
|
...logsContracts,
|
|
2135
|
-
...ciWaitContracts
|
|
2259
|
+
...ciWaitContracts,
|
|
2260
|
+
...evaluateContracts
|
|
2136
2261
|
].map((contract) => [contract.name, contract])
|
|
2137
2262
|
);
|
|
2138
2263
|
|
|
@@ -2570,6 +2695,7 @@ function registerListTasks(server2, conn2) {
|
|
|
2570
2695
|
assigneeId: z6.string().optional().describe("Filter by assigned user ID"),
|
|
2571
2696
|
unassigned: z6.boolean().optional().describe("Only return tasks with no assignee (mutually exclusive with assigneeId)"),
|
|
2572
2697
|
subProjectId: BOARD_FILTER,
|
|
2698
|
+
onHold: z6.boolean().optional().describe("true = only cards on hold, false = exclude them, omit = both"),
|
|
2573
2699
|
limit: z6.number().optional().describe("Max tasks to return (default 50)")
|
|
2574
2700
|
},
|
|
2575
2701
|
async (params) => {
|
|
@@ -2776,7 +2902,7 @@ function registerSearchTasks(server2, conn2) {
|
|
|
2776
2902
|
}
|
|
2777
2903
|
function registerListTags(server2, conn2) {
|
|
2778
2904
|
registerContractTool(server2, listTagsContract, async (params) => {
|
|
2779
|
-
const tags = await conn2.listTags(params.projectId);
|
|
2905
|
+
const tags = await conn2.listTags(params.projectId, params.detail);
|
|
2780
2906
|
return { content: [{ type: "text", text: JSON.stringify(tags, null, 2) }] };
|
|
2781
2907
|
});
|
|
2782
2908
|
}
|
|
@@ -3010,6 +3136,9 @@ var MIME_BY_EXT = {
|
|
|
3010
3136
|
".yaml": "application/yaml",
|
|
3011
3137
|
".yml": "application/yaml",
|
|
3012
3138
|
".pdf": "application/pdf",
|
|
3139
|
+
".docx": "application/vnd.openxmlformats-officedocument.wordprocessingml.document",
|
|
3140
|
+
".xlsx": "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet",
|
|
3141
|
+
".pptx": "application/vnd.openxmlformats-officedocument.presentationml.presentation",
|
|
3013
3142
|
".zip": "application/zip",
|
|
3014
3143
|
".gz": "application/gzip",
|
|
3015
3144
|
".mp4": "video/mp4",
|
|
@@ -3031,9 +3160,9 @@ function buildTextContent(file, content) {
|
|
|
3031
3160
|
const start = file.contentByteOffset ?? 0;
|
|
3032
3161
|
const shown = Buffer.byteLength(content, "utf-8");
|
|
3033
3162
|
const total = file.contentTotalBytes ?? shown;
|
|
3034
|
-
const more = file.contentTruncated ? ` \u2014 showing bytes ${start}\u2013${start + shown} of ${total}; pass offset=${start + shown} for the next page
|
|
3163
|
+
const more = file.contentTruncated ? ` \u2014 showing bytes ${start}\u2013${start + shown} of ${total}; pass offset=${start + shown} for the next page` : "";
|
|
3035
3164
|
const header = `${file.fileName ?? "file"} (${file.mimeType ?? "?"}, ${total} bytes)${more}
|
|
3036
|
-
${file.downloadUrl ? `downloadUrl: ${file.downloadUrl}
|
|
3165
|
+
${file.downloadUrl ? `downloadUrl (signed-in project members only; read bytes with this tool): ${file.downloadUrl}
|
|
3037
3166
|
` : ""}---
|
|
3038
3167
|
`;
|
|
3039
3168
|
return [{ type: "text", text: header + content }];
|
|
@@ -3159,10 +3288,24 @@ function registerSetFileTags(server2, conn2) {
|
|
|
3159
3288
|
}
|
|
3160
3289
|
);
|
|
3161
3290
|
}
|
|
3291
|
+
function registerDeleteAttachment(server2, conn2) {
|
|
3292
|
+
registerContractTool(server2, deleteAttachmentContract, async (params) => {
|
|
3293
|
+
const result = await conn2.deleteAttachment(params.taskId, params.fileId, params.projectId);
|
|
3294
|
+
return {
|
|
3295
|
+
content: [
|
|
3296
|
+
{
|
|
3297
|
+
type: "text",
|
|
3298
|
+
text: `Deleted ${result.fileName} (${result.id}) permanently. Any chat message that announced it keeps its text without the attachment.`
|
|
3299
|
+
}
|
|
3300
|
+
]
|
|
3301
|
+
};
|
|
3302
|
+
});
|
|
3303
|
+
}
|
|
3162
3304
|
function registerAttachmentTools(server2, conn2) {
|
|
3163
3305
|
registerListTaskFiles(server2, conn2);
|
|
3164
3306
|
registerGetAttachment(server2, conn2);
|
|
3165
3307
|
registerUploadAttachment(server2, conn2);
|
|
3308
|
+
registerDeleteAttachment(server2, conn2);
|
|
3166
3309
|
registerSetFileTags(server2, conn2);
|
|
3167
3310
|
}
|
|
3168
3311
|
|
|
@@ -3270,353 +3413,9 @@ function registerSuggestionTools(server2, conn2) {
|
|
|
3270
3413
|
});
|
|
3271
3414
|
}
|
|
3272
3415
|
|
|
3273
|
-
//
|
|
3274
|
-
function renderManualTestGroups(groups) {
|
|
3275
|
-
const lines = [];
|
|
3276
|
-
for (const g of groups) {
|
|
3277
|
-
lines.push(`## ${g.title} [${g.status}] (${g.slug})`);
|
|
3278
|
-
for (const t of g.tests) {
|
|
3279
|
-
const mark = t.status === "approved" ? "\u2713" : t.status === "rejected" ? "\u2717" : "\u25CB";
|
|
3280
|
-
lines.push(` ${mark} ${t.title} \u2014 ${t.status}`);
|
|
3281
|
-
if (t.status === "rejected") {
|
|
3282
|
-
for (const f2 of t.failures ?? []) {
|
|
3283
|
-
lines.push(` \u26A0 ${f2.userName ?? "Someone"}: ${f2.reason ?? "(no message)"}`);
|
|
3284
|
-
}
|
|
3285
|
-
}
|
|
3286
|
-
}
|
|
3287
|
-
lines.push("");
|
|
3288
|
-
}
|
|
3289
|
-
return lines.join("\n").trimEnd();
|
|
3290
|
-
}
|
|
3291
|
-
function registerListManualTests(server2, conn2) {
|
|
3292
|
-
registerContractTool(server2, listManualTestsContract, async (params) => {
|
|
3293
|
-
const items = await conn2.listManualTests(params.taskId, params.projectId);
|
|
3294
|
-
if (items.length === 0) {
|
|
3295
|
-
return { content: [{ type: "text", text: "No manual tests recorded for this task." }] };
|
|
3296
|
-
}
|
|
3297
|
-
const lines = items.flatMap((item, i) => {
|
|
3298
|
-
const checked = item.checked ? "[x]" : "[ ]";
|
|
3299
|
-
const row = [`${i + 1}. ${checked} ${item.title}`];
|
|
3300
|
-
for (const f2 of item.failures ?? []) {
|
|
3301
|
-
const who = f2.userName ?? "Someone";
|
|
3302
|
-
const reason = f2.reason ?? "(no message)";
|
|
3303
|
-
row.push(` \u26A0 Failed (${who}): ${reason}`);
|
|
3304
|
-
}
|
|
3305
|
-
return row;
|
|
3306
|
-
});
|
|
3307
|
-
return { content: [{ type: "text", text: lines.join("\n") }] };
|
|
3308
|
-
});
|
|
3309
|
-
}
|
|
3310
|
-
function registerSetManualTests(server2, conn2) {
|
|
3311
|
-
registerContractTool(server2, setManualTestsContract, async (params) => {
|
|
3312
|
-
const result = await conn2.setManualTests(params.taskId, params.items, params.projectId);
|
|
3313
|
-
const parts = [`Created ${result.created} manual test item(s).`];
|
|
3314
|
-
if (result.skipped > 0) parts.push(`Skipped ${result.skipped} duplicate(s).`);
|
|
3315
|
-
return { content: [{ type: "text", text: parts.join(" ") }] };
|
|
3316
|
-
});
|
|
3317
|
-
}
|
|
3318
|
-
function registerEditManualTest(server2, conn2) {
|
|
3319
|
-
registerContractTool(server2, editManualTestContract, async (params) => {
|
|
3320
|
-
await conn2.editManualTest(params.taskId, params.title, params.newTitle, params.projectId);
|
|
3321
|
-
return { content: [{ type: "text", text: `Updated manual test to "${params.newTitle}".` }] };
|
|
3322
|
-
});
|
|
3323
|
-
}
|
|
3324
|
-
function registerRemoveManualTest(server2, conn2) {
|
|
3325
|
-
registerContractTool(server2, removeManualTestContract, async (params) => {
|
|
3326
|
-
await conn2.removeManualTest(params.taskId, params.title, params.projectId);
|
|
3327
|
-
return { content: [{ type: "text", text: `Removed manual test "${params.title}".` }] };
|
|
3328
|
-
});
|
|
3329
|
-
}
|
|
3330
|
-
function registerApproveManualTest(server2, conn2) {
|
|
3331
|
-
registerContractTool(server2, approveManualTestContract, async (params) => {
|
|
3332
|
-
await conn2.approveManualTest(params.taskId, params.title, params.projectId);
|
|
3333
|
-
return { content: [{ type: "text", text: `Approved manual test "${params.title}".` }] };
|
|
3334
|
-
});
|
|
3335
|
-
}
|
|
3336
|
-
function registerRejectManualTest(server2, conn2) {
|
|
3337
|
-
registerContractTool(server2, rejectManualTestContract, async (params) => {
|
|
3338
|
-
await conn2.rejectManualTest(params.taskId, params.title, params.reason, params.projectId);
|
|
3339
|
-
return {
|
|
3340
|
-
content: [
|
|
3341
|
-
{
|
|
3342
|
-
type: "text",
|
|
3343
|
-
text: `Flagged an issue with manual test "${params.title}": ${params.reason}`
|
|
3344
|
-
}
|
|
3345
|
-
]
|
|
3346
|
-
};
|
|
3347
|
-
});
|
|
3348
|
-
}
|
|
3349
|
-
function registerQueryManualTests(server2, conn2) {
|
|
3350
|
-
registerContractTool(server2, queryManualTestsContract, async (params) => {
|
|
3351
|
-
const groups = await conn2.queryManualTests({
|
|
3352
|
-
projectId: params.projectId,
|
|
3353
|
-
cardStatuses: params.cardStatuses,
|
|
3354
|
-
testStatuses: params.testStatuses
|
|
3355
|
-
});
|
|
3356
|
-
if (groups.length === 0) {
|
|
3357
|
-
return { content: [{ type: "text", text: "No manual tests match those filters." }] };
|
|
3358
|
-
}
|
|
3359
|
-
return { content: [{ type: "text", text: renderManualTestGroups(groups) }] };
|
|
3360
|
-
});
|
|
3361
|
-
}
|
|
3362
|
-
function registerChecklistTools(server2, conn2) {
|
|
3363
|
-
registerListManualTests(server2, conn2);
|
|
3364
|
-
registerQueryManualTests(server2, conn2);
|
|
3365
|
-
registerSetManualTests(server2, conn2);
|
|
3366
|
-
registerEditManualTest(server2, conn2);
|
|
3367
|
-
registerRemoveManualTest(server2, conn2);
|
|
3368
|
-
registerApproveManualTest(server2, conn2);
|
|
3369
|
-
registerRejectManualTest(server2, conn2);
|
|
3370
|
-
}
|
|
3371
|
-
|
|
3372
|
-
// src/tools/workspace.ts
|
|
3416
|
+
// ../shared/dist/index.js
|
|
3373
3417
|
import { z as z9 } from "zod";
|
|
3374
|
-
|
|
3375
|
-
// src/workspace-ssh-tunnel.ts
|
|
3376
|
-
import net from "net";
|
|
3377
|
-
import { once } from "events";
|
|
3378
|
-
import { WebSocket } from "ws";
|
|
3379
|
-
var WORKSPACE_TUNNEL_HEARTBEAT_MS = 3e4;
|
|
3380
|
-
function withPort(url, port) {
|
|
3381
|
-
const parsed = new URL(url);
|
|
3382
|
-
parsed.searchParams.set("port", String(port));
|
|
3383
|
-
return parsed.toString();
|
|
3384
|
-
}
|
|
3385
|
-
function toBuffer(data) {
|
|
3386
|
-
if (Buffer.isBuffer(data)) return data;
|
|
3387
|
-
if (data instanceof ArrayBuffer) return Buffer.from(data);
|
|
3388
|
-
return Buffer.concat(data);
|
|
3389
|
-
}
|
|
3390
|
-
function startHeartbeat(ws) {
|
|
3391
|
-
const timer = setInterval(() => {
|
|
3392
|
-
if (ws.readyState === WebSocket.OPEN) ws.ping();
|
|
3393
|
-
}, WORKSPACE_TUNNEL_HEARTBEAT_MS);
|
|
3394
|
-
timer.unref?.();
|
|
3395
|
-
return () => clearInterval(timer);
|
|
3396
|
-
}
|
|
3397
|
-
function bridgeClient(client, options) {
|
|
3398
|
-
let ws = null;
|
|
3399
|
-
let ready = false;
|
|
3400
|
-
const pending = [];
|
|
3401
|
-
client.on("data", (data) => {
|
|
3402
|
-
const chunk = Buffer.isBuffer(data) ? data : Buffer.from(data);
|
|
3403
|
-
if (ready && ws && ws.readyState === WebSocket.OPEN) ws.send(chunk);
|
|
3404
|
-
else pending.push(chunk);
|
|
3405
|
-
});
|
|
3406
|
-
client.on("close", () => ws?.close());
|
|
3407
|
-
client.on("error", () => ws?.close());
|
|
3408
|
-
options.resolveTarget().then(({ tunnelUrl, attachToken }) => {
|
|
3409
|
-
const socket = new WebSocket(withPort(tunnelUrl, options.remotePort), {
|
|
3410
|
-
headers: { "x-workspace-attach-token": attachToken }
|
|
3411
|
-
});
|
|
3412
|
-
ws = socket;
|
|
3413
|
-
const stopHeartbeat = startHeartbeat(socket);
|
|
3414
|
-
socket.on("message", (data) => {
|
|
3415
|
-
const chunk = toBuffer(data);
|
|
3416
|
-
if (!ready) {
|
|
3417
|
-
const text = chunk.toString("utf8");
|
|
3418
|
-
if (text === JSON.stringify({ ok: true })) {
|
|
3419
|
-
ready = true;
|
|
3420
|
-
for (const buffered of pending.splice(0)) socket.send(buffered);
|
|
3421
|
-
return;
|
|
3422
|
-
}
|
|
3423
|
-
client.destroy(new Error(`Workspace tunnel rejected connection: ${text}`));
|
|
3424
|
-
socket.close();
|
|
3425
|
-
return;
|
|
3426
|
-
}
|
|
3427
|
-
client.write(chunk);
|
|
3428
|
-
});
|
|
3429
|
-
socket.on("close", () => {
|
|
3430
|
-
stopHeartbeat();
|
|
3431
|
-
client.destroy();
|
|
3432
|
-
});
|
|
3433
|
-
socket.on("error", (err) => {
|
|
3434
|
-
stopHeartbeat();
|
|
3435
|
-
client.destroy(err);
|
|
3436
|
-
});
|
|
3437
|
-
}).catch((err) => {
|
|
3438
|
-
client.destroy(err instanceof Error ? err : new Error(String(err)));
|
|
3439
|
-
});
|
|
3440
|
-
}
|
|
3441
|
-
async function startWorkspaceSshTunnel(options) {
|
|
3442
|
-
const host = options.host ?? "127.0.0.1";
|
|
3443
|
-
const server2 = net.createServer((client) => bridgeClient(client, options));
|
|
3444
|
-
const ac = new AbortController();
|
|
3445
|
-
server2.listen(options.preferredLocalPort ?? 0, host);
|
|
3446
|
-
try {
|
|
3447
|
-
await Promise.race([
|
|
3448
|
-
once(server2, "listening", { signal: ac.signal }),
|
|
3449
|
-
once(server2, "error", { signal: ac.signal }).then(([err]) => {
|
|
3450
|
-
throw err instanceof Error ? err : new Error(String(err));
|
|
3451
|
-
})
|
|
3452
|
-
]);
|
|
3453
|
-
} catch (err) {
|
|
3454
|
-
server2.close();
|
|
3455
|
-
throw new Error(
|
|
3456
|
-
`Failed to bind workspace SSH tunnel: ${err instanceof Error ? err.message : String(err)}`,
|
|
3457
|
-
{ cause: err }
|
|
3458
|
-
);
|
|
3459
|
-
} finally {
|
|
3460
|
-
ac.abort();
|
|
3461
|
-
}
|
|
3462
|
-
const address = server2.address();
|
|
3463
|
-
if (!address || typeof address === "string") {
|
|
3464
|
-
server2.close();
|
|
3465
|
-
throw new Error("Failed to bind workspace SSH tunnel");
|
|
3466
|
-
}
|
|
3467
|
-
return {
|
|
3468
|
-
host,
|
|
3469
|
-
port: address.port,
|
|
3470
|
-
close: () => new Promise((resolve, reject) => {
|
|
3471
|
-
server2.close((err) => {
|
|
3472
|
-
if (err) reject(err);
|
|
3473
|
-
else resolve();
|
|
3474
|
-
});
|
|
3475
|
-
})
|
|
3476
|
-
};
|
|
3477
|
-
}
|
|
3478
|
-
|
|
3479
|
-
// src/tools/workspace.ts
|
|
3480
|
-
var activeTunnels = /* @__PURE__ */ new Map();
|
|
3481
|
-
function createTunnelId(taskId, port) {
|
|
3482
|
-
return `${taskId}:${port}:${Date.now().toString(36)}:${Math.random().toString(36).slice(2, 8)}`;
|
|
3483
|
-
}
|
|
3484
|
-
function localUrlFor(port, host, localPort) {
|
|
3485
|
-
if (port === 2222) return void 0;
|
|
3486
|
-
return `http://${host}:${localPort}`;
|
|
3487
|
-
}
|
|
3488
|
-
function registerAttachInfoTool(server2, conn2) {
|
|
3489
|
-
server2.tool(
|
|
3490
|
-
"workspace_attach_info",
|
|
3491
|
-
"Return SSH/SFTP attach metadata for a running task Claudespace, plus hosted preview URLs and configured preview ports. Optionally installs an OpenSSH public key for this attach session.",
|
|
3492
|
-
{
|
|
3493
|
-
taskId: z9.string().describe("The task ID"),
|
|
3494
|
-
sshPublicKey: z9.string().optional().describe("Optional OpenSSH public key to install into the workspace")
|
|
3495
|
-
},
|
|
3496
|
-
async ({ taskId, sshPublicKey }) => {
|
|
3497
|
-
const info = await conn2.getWorkspaceAttachInfo(taskId, sshPublicKey);
|
|
3498
|
-
return { content: [{ type: "text", text: JSON.stringify(info, null, 2) }] };
|
|
3499
|
-
}
|
|
3500
|
-
);
|
|
3501
|
-
}
|
|
3502
|
-
function registerPreviewUrlsTool(server2, conn2) {
|
|
3503
|
-
server2.tool(
|
|
3504
|
-
"workspace_preview_urls",
|
|
3505
|
-
"Return the hosted preview URLs and preview ports for a running task Claudespace. This mirrors the web UI preview link metadata.",
|
|
3506
|
-
{
|
|
3507
|
-
taskId: z9.string().describe("The task ID")
|
|
3508
|
-
},
|
|
3509
|
-
async ({ taskId }) => {
|
|
3510
|
-
const info = await conn2.getWorkspaceAttachInfo(taskId);
|
|
3511
|
-
return {
|
|
3512
|
-
content: [
|
|
3513
|
-
{
|
|
3514
|
-
type: "text",
|
|
3515
|
-
text: JSON.stringify(
|
|
3516
|
-
{
|
|
3517
|
-
taskId: info.taskId,
|
|
3518
|
-
sessionId: info.sessionId,
|
|
3519
|
-
previewPorts: info.previewPorts,
|
|
3520
|
-
previewUrls: info.previewUrls
|
|
3521
|
-
},
|
|
3522
|
-
null,
|
|
3523
|
-
2
|
|
3524
|
-
)
|
|
3525
|
-
}
|
|
3526
|
-
]
|
|
3527
|
-
};
|
|
3528
|
-
}
|
|
3529
|
-
);
|
|
3530
|
-
}
|
|
3531
|
-
function registerStartTunnelTool(server2, conn2, startTunnel) {
|
|
3532
|
-
server2.tool(
|
|
3533
|
-
"workspace_start_tunnel",
|
|
3534
|
-
"Start a local loopback tunnel through the MCP server to a running task Claudespace port. Use port 2222 for SSH/SFTP, or one of previewPorts for app access.",
|
|
3535
|
-
{
|
|
3536
|
-
taskId: z9.string().describe("The task ID"),
|
|
3537
|
-
port: z9.number().optional().describe("Remote Claudespace port. Defaults to SSH port 2222."),
|
|
3538
|
-
preferredLocalPort: z9.number().optional().describe("Preferred local loopback port. If omitted, the OS chooses one."),
|
|
3539
|
-
sshPublicKey: z9.string().optional().describe("Optional OpenSSH public key to install before opening the tunnel")
|
|
3540
|
-
},
|
|
3541
|
-
async ({ taskId, port, preferredLocalPort, sshPublicKey }) => {
|
|
3542
|
-
const info = await conn2.getWorkspaceAttachInfo(taskId, sshPublicKey);
|
|
3543
|
-
if (!info.tunnelUrl || !info.attachToken) {
|
|
3544
|
-
throw new Error(
|
|
3545
|
-
"Workspace tunnels are unavailable for this task (GitHub Codespace-backed environments expose previews only \u2014 use workspace_preview_urls)."
|
|
3546
|
-
);
|
|
3547
|
-
}
|
|
3548
|
-
const remotePort = port ?? info.ssh?.remotePort ?? 2222;
|
|
3549
|
-
const handle = await startTunnel({
|
|
3550
|
-
remotePort,
|
|
3551
|
-
preferredLocalPort,
|
|
3552
|
-
// Re-mint per connection so the tunnel outlives the attach-token TTL.
|
|
3553
|
-
// The key (if any) is already installed by the call above; the refetch
|
|
3554
|
-
// only needs a fresh token, so it omits sshPublicKey.
|
|
3555
|
-
resolveTarget: async () => {
|
|
3556
|
-
const fresh = await conn2.getWorkspaceAttachInfo(taskId);
|
|
3557
|
-
if (!fresh.tunnelUrl || !fresh.attachToken)
|
|
3558
|
-
throw new Error("Workspace tunnel target is no longer available");
|
|
3559
|
-
return { tunnelUrl: fresh.tunnelUrl, attachToken: fresh.attachToken };
|
|
3560
|
-
}
|
|
3561
|
-
});
|
|
3562
|
-
const tunnelId = createTunnelId(taskId, remotePort);
|
|
3563
|
-
activeTunnels.set(tunnelId, handle);
|
|
3564
|
-
return {
|
|
3565
|
-
content: [
|
|
3566
|
-
{
|
|
3567
|
-
type: "text",
|
|
3568
|
-
text: JSON.stringify(
|
|
3569
|
-
{
|
|
3570
|
-
tunnelId,
|
|
3571
|
-
taskId,
|
|
3572
|
-
remotePort,
|
|
3573
|
-
localHost: handle.host,
|
|
3574
|
-
localPort: handle.port,
|
|
3575
|
-
localUrl: localUrlFor(remotePort, handle.host, handle.port),
|
|
3576
|
-
// `-o ServerAliveInterval=30` drives client-side keepalives so an
|
|
3577
|
-
// idle session keeps the tunnel warm (the tunnel layer pings too,
|
|
3578
|
-
// but the client must keep its own TCP path alive across every hop).
|
|
3579
|
-
sshCommand: remotePort === 2222 ? `ssh -o ServerAliveInterval=30 -o ServerAliveCountMax=3 -p ${handle.port} ${info.ssh?.username ?? "conveyor"}@${handle.host}` : void 0,
|
|
3580
|
-
sftpCommand: remotePort === 2222 ? `sftp -o ServerAliveInterval=30 -o ServerAliveCountMax=3 -P ${handle.port} ${info.ssh?.username ?? "conveyor"}@${handle.host}` : void 0,
|
|
3581
|
-
previewPorts: info.previewPorts,
|
|
3582
|
-
previewUrls: info.previewUrls,
|
|
3583
|
-
expiresAt: info.expiresAt
|
|
3584
|
-
},
|
|
3585
|
-
null,
|
|
3586
|
-
2
|
|
3587
|
-
)
|
|
3588
|
-
}
|
|
3589
|
-
]
|
|
3590
|
-
};
|
|
3591
|
-
}
|
|
3592
|
-
);
|
|
3593
|
-
}
|
|
3594
|
-
function registerStopTunnelTool(server2) {
|
|
3595
|
-
server2.tool(
|
|
3596
|
-
"workspace_stop_tunnel",
|
|
3597
|
-
"Stop a local workspace tunnel previously opened by workspace_start_tunnel.",
|
|
3598
|
-
{
|
|
3599
|
-
tunnelId: z9.string().describe("Tunnel id returned by workspace_start_tunnel")
|
|
3600
|
-
},
|
|
3601
|
-
async ({ tunnelId }) => {
|
|
3602
|
-
const tunnel = activeTunnels.get(tunnelId);
|
|
3603
|
-
if (!tunnel) throw new Error("Workspace tunnel not found");
|
|
3604
|
-
await tunnel.close();
|
|
3605
|
-
activeTunnels.delete(tunnelId);
|
|
3606
|
-
return { content: [{ type: "text", text: "Workspace tunnel stopped" }] };
|
|
3607
|
-
}
|
|
3608
|
-
);
|
|
3609
|
-
}
|
|
3610
|
-
function registerWorkspaceTools(server2, conn2, deps = {}) {
|
|
3611
|
-
registerAttachInfoTool(server2, conn2);
|
|
3612
|
-
registerPreviewUrlsTool(server2, conn2);
|
|
3613
|
-
registerStartTunnelTool(server2, conn2, deps.startTunnel ?? startWorkspaceSshTunnel);
|
|
3614
|
-
registerStopTunnelTool(server2);
|
|
3615
|
-
}
|
|
3616
|
-
|
|
3617
|
-
// ../shared/dist/index.js
|
|
3618
|
-
import { z as z10 } from "zod";
|
|
3619
|
-
import { z as z22 } from "zod";
|
|
3418
|
+
import { z as z23 } from "zod";
|
|
3620
3419
|
import { z as z32 } from "zod";
|
|
3621
3420
|
import { z as z42 } from "zod";
|
|
3622
3421
|
import { z as z52 } from "zod";
|
|
@@ -3624,7 +3423,7 @@ import { z as z62 } from "zod";
|
|
|
3624
3423
|
import { z as z72 } from "zod";
|
|
3625
3424
|
import { z as z82 } from "zod";
|
|
3626
3425
|
import { z as z92 } from "zod";
|
|
3627
|
-
import { z as
|
|
3426
|
+
import { z as z10 } from "zod";
|
|
3628
3427
|
import { z as z11 } from "zod";
|
|
3629
3428
|
import { z as z12 } from "zod";
|
|
3630
3429
|
var ACHIEVEMENT_RARITIES = [
|
|
@@ -3650,7 +3449,7 @@ var ACHIEVEMENT_RARITIES = [
|
|
|
3650
3449
|
{ key: "pack", name: "Pack", color: "#9c27b0", iconPath: "/storypoints/pack.svg" }
|
|
3651
3450
|
];
|
|
3652
3451
|
var RISK_LEVELS = ["critical", "high", "medium", "low"];
|
|
3653
|
-
var riskLevelSchema =
|
|
3452
|
+
var riskLevelSchema = z9.enum(RISK_LEVELS);
|
|
3654
3453
|
var DEFAULT_RISK_LEVELS = [
|
|
3655
3454
|
{
|
|
3656
3455
|
level: "critical",
|
|
@@ -3701,158 +3500,187 @@ var DEFAULT_TASK_STATUS_COLOR_INT = Number.parseInt(DEFAULT_TASK_STATUS_COLOR.sl
|
|
|
3701
3500
|
var MAX_FILE_SIZE_BYTES2 = 25 * 1024 * 1024;
|
|
3702
3501
|
var MAX_FILE_TAGS2 = 5;
|
|
3703
3502
|
var MAX_FILE_TAG_LENGTH = 100;
|
|
3503
|
+
var ALLOWED_FILE_MIME_TYPES = [
|
|
3504
|
+
"image/png",
|
|
3505
|
+
"image/jpeg",
|
|
3506
|
+
"image/gif",
|
|
3507
|
+
"image/webp",
|
|
3508
|
+
"application/json",
|
|
3509
|
+
"text/csv",
|
|
3510
|
+
"text/plain",
|
|
3511
|
+
"text/markdown",
|
|
3512
|
+
"text/vnd.mermaid",
|
|
3513
|
+
"application/pdf",
|
|
3514
|
+
// Reports (conveyor-consensus and friends) are single-file, no-JS HTML. Safe
|
|
3515
|
+
// to store because the DOWNLOAD route serves these under a sandbox CSP with
|
|
3516
|
+
// an opaque origin — see SANDBOXED_MIME in services/file/routes.ts.
|
|
3517
|
+
"text/html",
|
|
3518
|
+
// Office documents: the OOXML trio only (legacy .doc/.xls/.ppt stay out).
|
|
3519
|
+
// Served inline like PDFs; browsers download them and nothing renders
|
|
3520
|
+
// in-origin, so they need no SANDBOXED_MIME entry.
|
|
3521
|
+
"application/vnd.openxmlformats-officedocument.wordprocessingml.document",
|
|
3522
|
+
"application/vnd.openxmlformats-officedocument.spreadsheetml.sheet",
|
|
3523
|
+
"application/vnd.openxmlformats-officedocument.presentationml.presentation"
|
|
3524
|
+
];
|
|
3525
|
+
var ALLOWED_FILE_ACCEPT = [
|
|
3526
|
+
...ALLOWED_FILE_MIME_TYPES,
|
|
3527
|
+
// Extensions too: a mime-only `accept` filters the OS picker by its own mime
|
|
3528
|
+
// registry, which greys out .md/.mmd (and Office files on a machine without
|
|
3529
|
+
// Office) even though the upload would succeed.
|
|
3530
|
+
".png,.jpg,.jpeg,.gif,.webp,.json,.csv,.txt,.md,.mmd,.pdf,.html,.docx,.xlsx,.pptx"
|
|
3531
|
+
].join(",");
|
|
3704
3532
|
var EMBED_THRESHOLD_IMAGES = 5 * 1024 * 1024;
|
|
3705
3533
|
var EMBED_THRESHOLD_TEXT = 2 * 1024 * 1024;
|
|
3706
3534
|
var IDLE_HEARTBEAT_MS = 90 * 1e3;
|
|
3707
|
-
var TurnEndToolCallSchema =
|
|
3708
|
-
tool:
|
|
3709
|
-
input:
|
|
3710
|
-
output:
|
|
3711
|
-
timestamp:
|
|
3535
|
+
var TurnEndToolCallSchema = z23.object({
|
|
3536
|
+
tool: z23.string(),
|
|
3537
|
+
input: z23.string().optional(),
|
|
3538
|
+
output: z23.string().optional(),
|
|
3539
|
+
timestamp: z23.string().optional()
|
|
3712
3540
|
}).passthrough();
|
|
3713
|
-
var AgentTurnUsageSchema =
|
|
3714
|
-
inputTokens:
|
|
3715
|
-
outputTokens:
|
|
3716
|
-
cacheReadInputTokens:
|
|
3717
|
-
cacheCreationInputTokens:
|
|
3718
|
-
totalTokens:
|
|
3719
|
-
});
|
|
3720
|
-
var KnownAgentEventSchema =
|
|
3541
|
+
var AgentTurnUsageSchema = z23.object({
|
|
3542
|
+
inputTokens: z23.number(),
|
|
3543
|
+
outputTokens: z23.number(),
|
|
3544
|
+
cacheReadInputTokens: z23.number(),
|
|
3545
|
+
cacheCreationInputTokens: z23.number(),
|
|
3546
|
+
totalTokens: z23.number()
|
|
3547
|
+
});
|
|
3548
|
+
var KnownAgentEventSchema = z23.discriminatedUnion("type", [
|
|
3721
3549
|
// ── Lifecycle / connection ────────────────────────────────────────────
|
|
3722
|
-
|
|
3723
|
-
type:
|
|
3724
|
-
sessionId:
|
|
3725
|
-
projectId:
|
|
3550
|
+
z23.object({
|
|
3551
|
+
type: z23.literal("connected"),
|
|
3552
|
+
sessionId: z23.string(),
|
|
3553
|
+
projectId: z23.string().optional()
|
|
3726
3554
|
}).passthrough(),
|
|
3727
3555
|
// Open-ended context snapshot spread from buildInitializationContext().
|
|
3728
|
-
|
|
3729
|
-
|
|
3730
|
-
type:
|
|
3731
|
-
reason:
|
|
3732
|
-
attempt:
|
|
3733
|
-
attempts:
|
|
3556
|
+
z23.object({ type: z23.literal("session_manifest") }).passthrough(),
|
|
3557
|
+
z23.object({
|
|
3558
|
+
type: z23.literal("agent_runner_status"),
|
|
3559
|
+
reason: z23.string(),
|
|
3560
|
+
attempt: z23.number().optional(),
|
|
3561
|
+
attempts: z23.number().optional()
|
|
3734
3562
|
}).passthrough(),
|
|
3735
|
-
|
|
3736
|
-
|
|
3737
|
-
|
|
3563
|
+
z23.object({ type: z23.literal("shutdown"), reason: z23.string().optional() }).passthrough(),
|
|
3564
|
+
z23.object({ type: z23.literal("mode_changed"), agentMode: z23.string() }).passthrough(),
|
|
3565
|
+
z23.object({ type: z23.literal("mode_transition"), from: z23.string(), to: z23.string() }).passthrough(),
|
|
3738
3566
|
// ── Turn stream ───────────────────────────────────────────────────────
|
|
3739
|
-
|
|
3740
|
-
|
|
3741
|
-
|
|
3742
|
-
type:
|
|
3743
|
-
tool:
|
|
3567
|
+
z23.object({ type: z23.literal("message"), content: z23.string() }).passthrough(),
|
|
3568
|
+
z23.object({ type: z23.literal("thinking"), message: z23.string() }).passthrough(),
|
|
3569
|
+
z23.object({
|
|
3570
|
+
type: z23.literal("tool_use"),
|
|
3571
|
+
tool: z23.string(),
|
|
3744
3572
|
// Producers send JSON.stringify(input); consumers defend against
|
|
3745
3573
|
// object inputs from older agents, so the wire stays permissive here.
|
|
3746
|
-
input:
|
|
3574
|
+
input: z23.unknown().optional()
|
|
3747
3575
|
}).passthrough(),
|
|
3748
|
-
|
|
3749
|
-
type:
|
|
3750
|
-
tool:
|
|
3751
|
-
output:
|
|
3752
|
-
isError:
|
|
3753
|
-
redactedCount:
|
|
3576
|
+
z23.object({
|
|
3577
|
+
type: z23.literal("tool_result"),
|
|
3578
|
+
tool: z23.string(),
|
|
3579
|
+
output: z23.unknown().optional(),
|
|
3580
|
+
isError: z23.boolean().optional(),
|
|
3581
|
+
redactedCount: z23.number().optional()
|
|
3754
3582
|
}).passthrough(),
|
|
3755
|
-
|
|
3756
|
-
|
|
3757
|
-
type:
|
|
3758
|
-
summary:
|
|
3759
|
-
durationMs:
|
|
3583
|
+
z23.object({ type: z23.literal("turn_end"), toolCalls: z23.array(TurnEndToolCallSchema) }).passthrough(),
|
|
3584
|
+
z23.object({
|
|
3585
|
+
type: z23.literal("completed"),
|
|
3586
|
+
summary: z23.string().optional(),
|
|
3587
|
+
durationMs: z23.number().optional(),
|
|
3760
3588
|
/** Tokens this turn consumed, summed from the transcript's assistant
|
|
3761
3589
|
* records. Optional: an older agent build sends none, and the server
|
|
3762
3590
|
* then records nothing rather than zeroing the session's totals. */
|
|
3763
3591
|
usage: AgentTurnUsageSchema.optional()
|
|
3764
3592
|
}).passthrough(),
|
|
3765
|
-
|
|
3766
|
-
|
|
3767
|
-
|
|
3593
|
+
z23.object({ type: z23.literal("error"), message: z23.string() }).passthrough(),
|
|
3594
|
+
z23.object({ type: z23.literal("agent_typing_start") }).passthrough(),
|
|
3595
|
+
z23.object({ type: z23.literal("agent_typing_stop") }).passthrough(),
|
|
3768
3596
|
// ── Telemetry ─────────────────────────────────────────────────────────
|
|
3769
3597
|
// heartbeat/typing: legacy telemetry the server still classifies as
|
|
3770
3598
|
// transient (TRANSIENT_EVENT_TYPES) — kept in the vocabulary.
|
|
3771
|
-
|
|
3772
|
-
|
|
3773
|
-
|
|
3774
|
-
type:
|
|
3775
|
-
contextTokens:
|
|
3776
|
-
contextWindow:
|
|
3777
|
-
inputTokens:
|
|
3778
|
-
cacheReadInputTokens:
|
|
3779
|
-
cacheCreationInputTokens:
|
|
3780
|
-
totalTokensUsed:
|
|
3599
|
+
z23.object({ type: z23.literal("heartbeat") }).passthrough(),
|
|
3600
|
+
z23.object({ type: z23.literal("typing") }).passthrough(),
|
|
3601
|
+
z23.object({
|
|
3602
|
+
type: z23.literal("context_update"),
|
|
3603
|
+
contextTokens: z23.number(),
|
|
3604
|
+
contextWindow: z23.number(),
|
|
3605
|
+
inputTokens: z23.number().optional(),
|
|
3606
|
+
cacheReadInputTokens: z23.number().optional(),
|
|
3607
|
+
cacheCreationInputTokens: z23.number().optional(),
|
|
3608
|
+
totalTokensUsed: z23.number().optional()
|
|
3781
3609
|
}).passthrough(),
|
|
3782
3610
|
// Four producer shapes share this type: {rateLimitType, utilization, status}
|
|
3783
3611
|
// (SDK rate_limit_event), {resetsAt} (agent-connection resume notice), the
|
|
3784
3612
|
// usage-sampler ({rateLimitType, utilization, status, resetsAt, gauges}
|
|
3785
3613
|
// — resetsAt matches rateLimitType; gauges survives via .passthrough()), and
|
|
3786
3614
|
// {unmeasurable, reason} (the sampler reporting it cannot read this key).
|
|
3787
|
-
|
|
3788
|
-
type:
|
|
3789
|
-
rateLimitType:
|
|
3790
|
-
utilization:
|
|
3791
|
-
status:
|
|
3792
|
-
resetsAt:
|
|
3793
|
-
unmeasurable:
|
|
3794
|
-
reason:
|
|
3615
|
+
z23.object({
|
|
3616
|
+
type: z23.literal("rate_limit_update"),
|
|
3617
|
+
rateLimitType: z23.string().optional(),
|
|
3618
|
+
utilization: z23.number().optional(),
|
|
3619
|
+
status: z23.string().optional(),
|
|
3620
|
+
resetsAt: z23.string().optional(),
|
|
3621
|
+
unmeasurable: z23.boolean().optional(),
|
|
3622
|
+
reason: z23.string().optional()
|
|
3795
3623
|
}).passthrough(),
|
|
3796
|
-
|
|
3797
|
-
type:
|
|
3798
|
-
trigger:
|
|
3799
|
-
preTokens:
|
|
3624
|
+
z23.object({
|
|
3625
|
+
type: z23.literal("context_compacted"),
|
|
3626
|
+
trigger: z23.string().optional(),
|
|
3627
|
+
preTokens: z23.number().optional()
|
|
3800
3628
|
}).passthrough(),
|
|
3801
|
-
|
|
3802
|
-
type:
|
|
3803
|
-
toolName:
|
|
3804
|
-
elapsedSeconds:
|
|
3629
|
+
z23.object({
|
|
3630
|
+
type: z23.literal("tool_progress"),
|
|
3631
|
+
toolName: z23.string().optional(),
|
|
3632
|
+
elapsedSeconds: z23.number().optional()
|
|
3805
3633
|
}).passthrough(),
|
|
3806
|
-
|
|
3807
|
-
type:
|
|
3808
|
-
sdkTaskId:
|
|
3809
|
-
description:
|
|
3634
|
+
z23.object({
|
|
3635
|
+
type: z23.literal("subagent_started"),
|
|
3636
|
+
sdkTaskId: z23.string().optional(),
|
|
3637
|
+
description: z23.string().optional()
|
|
3810
3638
|
}).passthrough(),
|
|
3811
|
-
|
|
3812
|
-
type:
|
|
3813
|
-
sdkTaskId:
|
|
3814
|
-
description:
|
|
3815
|
-
toolUses:
|
|
3816
|
-
durationMs:
|
|
3639
|
+
z23.object({
|
|
3640
|
+
type: z23.literal("subagent_progress"),
|
|
3641
|
+
sdkTaskId: z23.string().optional(),
|
|
3642
|
+
description: z23.string().optional(),
|
|
3643
|
+
toolUses: z23.number().optional(),
|
|
3644
|
+
durationMs: z23.number().optional()
|
|
3817
3645
|
}).passthrough(),
|
|
3818
3646
|
// ── Work products ─────────────────────────────────────────────────────
|
|
3819
|
-
|
|
3820
|
-
|
|
3821
|
-
type:
|
|
3822
|
-
result:
|
|
3823
|
-
summary:
|
|
3824
|
-
issues:
|
|
3825
|
-
|
|
3826
|
-
file:
|
|
3827
|
-
line:
|
|
3828
|
-
severity:
|
|
3829
|
-
description:
|
|
3647
|
+
z23.object({ type: z23.literal("pr_created"), url: z23.string(), number: z23.number() }).passthrough(),
|
|
3648
|
+
z23.object({
|
|
3649
|
+
type: z23.literal("code_review_complete"),
|
|
3650
|
+
result: z23.enum(["approved", "changes_requested"]),
|
|
3651
|
+
summary: z23.string().optional(),
|
|
3652
|
+
issues: z23.array(
|
|
3653
|
+
z23.object({
|
|
3654
|
+
file: z23.string(),
|
|
3655
|
+
line: z23.number().optional(),
|
|
3656
|
+
severity: z23.string().optional(),
|
|
3657
|
+
description: z23.string().optional()
|
|
3830
3658
|
}).passthrough()
|
|
3831
3659
|
).optional()
|
|
3832
3660
|
}).passthrough(),
|
|
3833
3661
|
// ── Environment setup / start command ─────────────────────────────────
|
|
3834
|
-
|
|
3835
|
-
|
|
3836
|
-
type:
|
|
3837
|
-
startCommandRunning:
|
|
3838
|
-
startCommandConfigured:
|
|
3662
|
+
z23.object({ type: z23.literal("setup_output"), stream: z23.string(), data: z23.string() }).passthrough(),
|
|
3663
|
+
z23.object({
|
|
3664
|
+
type: z23.literal("setup_complete"),
|
|
3665
|
+
startCommandRunning: z23.boolean().optional(),
|
|
3666
|
+
startCommandConfigured: z23.boolean().optional(),
|
|
3839
3667
|
// Sanitized server-side by sanitizeSessionPreviewPorts — stays unknown.
|
|
3840
|
-
previewPorts:
|
|
3668
|
+
previewPorts: z23.unknown().optional()
|
|
3841
3669
|
}).passthrough(),
|
|
3842
|
-
|
|
3843
|
-
|
|
3844
|
-
|
|
3845
|
-
|
|
3846
|
-
type:
|
|
3847
|
-
code:
|
|
3848
|
-
signal:
|
|
3849
|
-
message:
|
|
3670
|
+
z23.object({ type: z23.literal("setup_error"), message: z23.string() }).passthrough(),
|
|
3671
|
+
z23.object({ type: z23.literal("start_command_started") }).passthrough(),
|
|
3672
|
+
z23.object({ type: z23.literal("start_command_output"), stream: z23.string(), data: z23.string() }).passthrough(),
|
|
3673
|
+
z23.object({
|
|
3674
|
+
type: z23.literal("start_command_exited"),
|
|
3675
|
+
code: z23.number().nullable().optional(),
|
|
3676
|
+
signal: z23.string().nullable().optional(),
|
|
3677
|
+
message: z23.string().optional()
|
|
3850
3678
|
}).passthrough(),
|
|
3851
|
-
|
|
3679
|
+
z23.object({ type: z23.literal("start_command_error"), message: z23.string() }).passthrough()
|
|
3852
3680
|
]);
|
|
3853
|
-
var AgentEventSchema =
|
|
3681
|
+
var AgentEventSchema = z23.union([
|
|
3854
3682
|
KnownAgentEventSchema,
|
|
3855
|
-
|
|
3683
|
+
z23.object({ type: z23.string().min(1) }).catchall(z23.unknown())
|
|
3856
3684
|
]);
|
|
3857
3685
|
var cardDescription = z32.string().max(CARD_DESCRIPTION_MAX, CARD_DESCRIPTION_LIMIT_MESSAGE).optional();
|
|
3858
3686
|
var AgentHeartbeatSchema = z32.object({
|
|
@@ -3896,9 +3724,15 @@ var GetChatMessagesRequestSchema = z32.object({
|
|
|
3896
3724
|
taskId: z32.string().optional()
|
|
3897
3725
|
});
|
|
3898
3726
|
var GetTaskFilesRequestSchema = z32.object({
|
|
3899
|
-
sessionId: z32.string()
|
|
3727
|
+
sessionId: z32.string(),
|
|
3728
|
+
taskId: z32.string().optional()
|
|
3900
3729
|
});
|
|
3901
3730
|
var GetTaskFileRequestSchema = z32.object({
|
|
3731
|
+
sessionId: z32.string(),
|
|
3732
|
+
fileId: z32.string(),
|
|
3733
|
+
taskId: z32.string().optional()
|
|
3734
|
+
});
|
|
3735
|
+
var DeleteTaskFileRequestSchema = z32.object({
|
|
3902
3736
|
sessionId: z32.string(),
|
|
3903
3737
|
fileId: z32.string()
|
|
3904
3738
|
});
|
|
@@ -3963,7 +3797,7 @@ var StoreSessionIdRequestSchema = z32.object({
|
|
|
3963
3797
|
});
|
|
3964
3798
|
var SetManualTestsRequestSchema = z32.object({
|
|
3965
3799
|
sessionId: z32.string(),
|
|
3966
|
-
items:
|
|
3800
|
+
items: ManualTestItemsSchema
|
|
3967
3801
|
});
|
|
3968
3802
|
var EditManualTestRequestSchema = z32.object({
|
|
3969
3803
|
sessionId: z32.string(),
|
|
@@ -4426,6 +4260,11 @@ var ListProjectTasksRequestSchema = z52.object({
|
|
|
4426
4260
|
// Scope to a sub-project board when provided. Unlike the board layer's `?? null`
|
|
4427
4261
|
// semantics, agents default to seeing the whole project when omitted.
|
|
4428
4262
|
subProjectId: z52.string().nullable().optional(),
|
|
4263
|
+
// Plain equality on the on-hold flag: true = only parked cards, false =
|
|
4264
|
+
// exclude them, omitted = both. Deliberately NOT the board's
|
|
4265
|
+
// includeOnHold/surfaceActiveWork bucketing — an agent sweeping a backlog
|
|
4266
|
+
// needs the raw column, not a column projection.
|
|
4267
|
+
onHold: z52.boolean().optional(),
|
|
4429
4268
|
limit: z52.number().int().positive().optional().default(50)
|
|
4430
4269
|
}).refine((p) => !(p.unassigned && p.assigneeId), {
|
|
4431
4270
|
message: "Pass either assigneeId or unassigned, not both"
|
|
@@ -4460,7 +4299,9 @@ var SearchProjectTasksRequestSchema = z52.object({
|
|
|
4460
4299
|
message: "Pass either assigneeId or unassigned, not both"
|
|
4461
4300
|
});
|
|
4462
4301
|
var ListProjectTagsRequestSchema = z52.object({
|
|
4463
|
-
projectId: z52.string()
|
|
4302
|
+
projectId: z52.string(),
|
|
4303
|
+
/** "summary" (default) omits inline context links; "full" includes them. */
|
|
4304
|
+
detail: z52.enum(["summary", "full"]).optional()
|
|
4464
4305
|
});
|
|
4465
4306
|
var GetProjectTagRequestSchema = z52.object({
|
|
4466
4307
|
projectId: z52.string(),
|
|
@@ -4563,11 +4404,15 @@ var UpdateProjectTaskRequestSchema = z52.object({
|
|
|
4563
4404
|
// handler: the ref must exist on origin and no live workspace may be bound
|
|
4564
4405
|
// to a different branch.
|
|
4565
4406
|
githubBranch: z52.string().min(1).max(GIT_BRANCH_NAME_MAX).refine(isValidGitBranchName, { message: GIT_BRANCH_NAME_MESSAGE }).nullable().optional(),
|
|
4407
|
+
// Park the card on hold (true) or return it to the active board (false).
|
|
4408
|
+
// The handler mirrors the web path's two side effects: a board re-bucket
|
|
4409
|
+
// event, and demotion to Open when an active-work card is parked.
|
|
4410
|
+
onHold: z52.boolean().optional(),
|
|
4566
4411
|
requestingUserId: z52.string().optional()
|
|
4567
4412
|
}).strict().refine(
|
|
4568
|
-
(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,
|
|
4413
|
+
(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 || v.onHold !== void 0,
|
|
4569
4414
|
{
|
|
4570
|
-
message: "update_task requires at least one field to change (title, description, plan, status, risk, storyPointValue, assignedUserId, subProjectId, or
|
|
4415
|
+
message: "update_task requires at least one field to change (title, description, plan, status, risk, storyPointValue, assignedUserId, subProjectId, githubBranch, or onHold)"
|
|
4571
4416
|
}
|
|
4572
4417
|
);
|
|
4573
4418
|
var TransitionProjectTaskStatusRequestSchema = z52.object({
|
|
@@ -4828,6 +4673,12 @@ var GetProjectAttachmentRequestSchema = z52.object({
|
|
|
4828
4673
|
/** Max bytes of text content to return from `offset`. Server default applies. */
|
|
4829
4674
|
maxBytes: z52.number().int().positive().optional()
|
|
4830
4675
|
});
|
|
4676
|
+
var DeleteProjectTaskFileRequestSchema = z52.object({
|
|
4677
|
+
projectId: z52.string(),
|
|
4678
|
+
taskId: z52.string(),
|
|
4679
|
+
fileId: z52.string(),
|
|
4680
|
+
requestingUserId: z52.string().optional()
|
|
4681
|
+
});
|
|
4831
4682
|
var RequestProjectFileUploadRequestSchema = z52.object({
|
|
4832
4683
|
projectId: z52.string(),
|
|
4833
4684
|
taskId: z52.string(),
|
|
@@ -4882,7 +4733,7 @@ var QueryProjectManualTestsRequestSchema = z52.object({
|
|
|
4882
4733
|
var SetProjectManualTestsRequestSchema = z52.object({
|
|
4883
4734
|
projectId: z52.string(),
|
|
4884
4735
|
taskId: z52.string(),
|
|
4885
|
-
items:
|
|
4736
|
+
items: ManualTestItemsSchema,
|
|
4886
4737
|
requestingUserId: z52.string().optional()
|
|
4887
4738
|
});
|
|
4888
4739
|
var EditProjectManualTestRequestSchema = z52.object({
|
|
@@ -4961,170 +4812,134 @@ var ReportApiOutageRequestSchema = z72.object({
|
|
|
4961
4812
|
detail: z72.string().max(2e3),
|
|
4962
4813
|
attempts: z72.number().int().min(0).max(100)
|
|
4963
4814
|
});
|
|
4964
|
-
var SHA_PATTERN = /^[0-9a-f]{40}$/i;
|
|
4965
|
-
var ReviewGuideFileReferenceSchema = z82.object({
|
|
4966
|
-
path: z82.string().min(1).max(500),
|
|
4967
|
-
startLine: z82.number().int().positive().max(1e6).optional(),
|
|
4968
|
-
endLine: z82.number().int().positive().max(1e6).optional(),
|
|
4969
|
-
hunkHeader: z82.string().min(1).max(300).optional()
|
|
4970
|
-
}).strict().superRefine((value, ctx) => {
|
|
4971
|
-
if (value.endLine !== void 0 && value.startLine === void 0) {
|
|
4972
|
-
ctx.addIssue({
|
|
4973
|
-
code: "custom",
|
|
4974
|
-
path: ["startLine"],
|
|
4975
|
-
message: "startLine is required when endLine is set"
|
|
4976
|
-
});
|
|
4977
|
-
}
|
|
4978
|
-
if (value.startLine !== void 0 && value.endLine !== void 0 && value.endLine < value.startLine) {
|
|
4979
|
-
ctx.addIssue({
|
|
4980
|
-
code: "custom",
|
|
4981
|
-
path: ["endLine"],
|
|
4982
|
-
message: "endLine must be greater than or equal to startLine"
|
|
4983
|
-
});
|
|
4984
|
-
}
|
|
4985
|
-
});
|
|
4986
|
-
var ReviewGuideSectionSchema = z82.object({
|
|
4987
|
-
title: z82.string().min(1).max(160),
|
|
4988
|
-
explanation: z82.string().min(1).max(2e3),
|
|
4989
|
-
classification: z82.enum(["core", "supporting"]).optional(),
|
|
4990
|
-
files: z82.array(ReviewGuideFileReferenceSchema).min(1).max(20)
|
|
4991
|
-
}).strict();
|
|
4992
|
-
var ReviewGuideContentSchema = z82.object({
|
|
4993
|
-
overview: z82.string().min(1).max(3e3),
|
|
4994
|
-
sections: z82.array(ReviewGuideSectionSchema).min(1).max(12)
|
|
4995
|
-
}).strict();
|
|
4996
|
-
var PublishReviewGuideRequestSchema = ReviewGuideContentSchema.extend({
|
|
4997
|
-
sessionId: z82.string().min(1),
|
|
4998
|
-
reviewedSha: z82.string().regex(SHA_PATTERN, "reviewedSha must be a 40-character commit SHA")
|
|
4999
|
-
}).strict();
|
|
5000
4815
|
var CONTEXT_LINK_LOCATOR_MAX2 = 300;
|
|
5001
4816
|
var TAG_DESCRIPTION_MAX = CARD_DESCRIPTION_MAX;
|
|
5002
4817
|
var TAG_OVERVIEW_MAX = 32e3;
|
|
5003
4818
|
var TAG_REASON_MAX = 500;
|
|
5004
|
-
var ProjectTagContextPathSchema =
|
|
5005
|
-
type:
|
|
5006
|
-
path:
|
|
5007
|
-
label:
|
|
4819
|
+
var ProjectTagContextPathSchema = z82.object({
|
|
4820
|
+
type: z82.enum(["rule", "doc", "file", "folder"]),
|
|
4821
|
+
path: z82.string().min(1).max(500),
|
|
4822
|
+
label: z82.string().max(100).optional(),
|
|
5008
4823
|
/** Verified-link tether — text that must keep existing in the file. */
|
|
5009
|
-
locator:
|
|
4824
|
+
locator: z82.string().min(1).max(CONTEXT_LINK_LOCATOR_MAX2).regex(/^[^\r\n]*$/, "Locator cannot contain line breaks").optional(),
|
|
5010
4825
|
/** test = must appear in a real test/describe title; code = any substring. */
|
|
5011
|
-
locatorType:
|
|
4826
|
+
locatorType: z82.enum(["test", "code"]).optional()
|
|
5012
4827
|
}).refine((link) => link.locator === void 0 === (link.locatorType === void 0), {
|
|
5013
4828
|
message: "locator and locatorType must be provided together"
|
|
5014
4829
|
}).refine((link) => link.locator === void 0 || link.type !== "folder", {
|
|
5015
4830
|
message: "folder links cannot carry a locator"
|
|
5016
4831
|
});
|
|
5017
|
-
var hexColor =
|
|
5018
|
-
var overviewPathSchema =
|
|
5019
|
-
var CreateProjectTagRequestSchema =
|
|
5020
|
-
projectId:
|
|
5021
|
-
name:
|
|
4832
|
+
var hexColor = z82.string().regex(/^#[0-9a-fA-F]{6}$/, "Expected #RRGGBB hex color");
|
|
4833
|
+
var overviewPathSchema = z82.string().min(1).max(500).regex(/^[^\r\n]*$/, "Overview path cannot contain line breaks");
|
|
4834
|
+
var CreateProjectTagRequestSchema = z82.object({
|
|
4835
|
+
projectId: z82.string(),
|
|
4836
|
+
name: z82.string().min(1).max(50),
|
|
5022
4837
|
color: hexColor.optional(),
|
|
5023
|
-
description:
|
|
5024
|
-
overview:
|
|
4838
|
+
description: z82.string().max(TAG_DESCRIPTION_MAX).optional(),
|
|
4839
|
+
overview: z82.string().max(TAG_OVERVIEW_MAX).optional(),
|
|
5025
4840
|
/** Source the overview from this repo file (stored overview stays as the pending fallback). */
|
|
5026
4841
|
overviewPath: overviewPathSchema.optional(),
|
|
5027
|
-
contextPaths:
|
|
4842
|
+
contextPaths: z82.array(ProjectTagContextPathSchema).max(20).optional(),
|
|
5028
4843
|
/** Parents to link at create time (multi-parent DAG). */
|
|
5029
|
-
parentTagIds:
|
|
5030
|
-
requestingUserId:
|
|
4844
|
+
parentTagIds: z82.array(z82.string()).max(25).optional(),
|
|
4845
|
+
requestingUserId: z82.string().optional()
|
|
5031
4846
|
});
|
|
5032
|
-
var UpdateProjectTagRequestSchema =
|
|
5033
|
-
projectId:
|
|
5034
|
-
tagId:
|
|
5035
|
-
name:
|
|
4847
|
+
var UpdateProjectTagRequestSchema = z82.object({
|
|
4848
|
+
projectId: z82.string(),
|
|
4849
|
+
tagId: z82.string(),
|
|
4850
|
+
name: z82.string().min(1).max(50).optional(),
|
|
5036
4851
|
color: hexColor.optional(),
|
|
5037
|
-
description:
|
|
4852
|
+
description: z82.string().max(TAG_DESCRIPTION_MAX).optional(),
|
|
5038
4853
|
/** Full markdown glossary body; null clears it. Rejected while overviewPath is set. */
|
|
5039
|
-
overview:
|
|
4854
|
+
overview: z82.string().max(TAG_OVERVIEW_MAX).nullable().optional(),
|
|
5040
4855
|
/** Repo file to source the overview from; null clears back to the stored overview. */
|
|
5041
4856
|
overviewPath: overviewPathSchema.nullable().optional(),
|
|
5042
4857
|
/** Full replacement of the tag's context links when provided. */
|
|
5043
|
-
contextPaths:
|
|
4858
|
+
contextPaths: z82.array(ProjectTagContextPathSchema).max(20).optional(),
|
|
5044
4859
|
/** Full-set replacement of the tag's parent tags (multi-parent DAG). */
|
|
5045
|
-
parentTagIds:
|
|
4860
|
+
parentTagIds: z82.array(z82.string()).max(25).optional(),
|
|
5046
4861
|
/** One-line revision provenance, recorded in the tag's history. */
|
|
5047
|
-
reason:
|
|
4862
|
+
reason: z82.string().max(TAG_REASON_MAX).optional(),
|
|
5048
4863
|
/** Card the caller was working in — stamped into the revision history. */
|
|
5049
|
-
taskId:
|
|
5050
|
-
requestingUserId:
|
|
4864
|
+
taskId: z82.string().optional(),
|
|
4865
|
+
requestingUserId: z82.string().optional()
|
|
5051
4866
|
});
|
|
5052
|
-
var PostToProjectChatRequestSchema =
|
|
5053
|
-
projectId:
|
|
5054
|
-
content:
|
|
5055
|
-
requestingUserId:
|
|
4867
|
+
var PostToProjectChatRequestSchema = z82.object({
|
|
4868
|
+
projectId: z82.string(),
|
|
4869
|
+
content: z82.string().min(1).max(2e4),
|
|
4870
|
+
requestingUserId: z82.string().optional(),
|
|
5056
4871
|
/** Marks the post so the server can persist it beyond chat (tag-audit summaries land in tag history). */
|
|
5057
|
-
kind:
|
|
5058
|
-
});
|
|
5059
|
-
var StartTagAuditRequestSchema =
|
|
5060
|
-
projectId:
|
|
5061
|
-
requestingUserId:
|
|
5062
|
-
});
|
|
5063
|
-
var StartTaskAuditRequestSchema =
|
|
5064
|
-
projectId:
|
|
5065
|
-
taskIds:
|
|
5066
|
-
requestingUserId:
|
|
5067
|
-
});
|
|
5068
|
-
var GetActiveAuditSessionsRequestSchema =
|
|
5069
|
-
projectId:
|
|
5070
|
-
});
|
|
5071
|
-
var ReportTaskAuditResultRequestSchema =
|
|
5072
|
-
projectId:
|
|
5073
|
-
taskId:
|
|
5074
|
-
summary:
|
|
5075
|
-
turnGrades:
|
|
5076
|
-
|
|
5077
|
-
turnIndex:
|
|
5078
|
-
phase:
|
|
5079
|
-
grade:
|
|
5080
|
-
reasoning:
|
|
5081
|
-
eventType:
|
|
5082
|
-
eventSummary:
|
|
4872
|
+
kind: z82.enum(["tag_audit_summary"]).optional()
|
|
4873
|
+
});
|
|
4874
|
+
var StartTagAuditRequestSchema = z82.object({
|
|
4875
|
+
projectId: z82.string(),
|
|
4876
|
+
requestingUserId: z82.string().optional()
|
|
4877
|
+
});
|
|
4878
|
+
var StartTaskAuditRequestSchema = z82.object({
|
|
4879
|
+
projectId: z82.string(),
|
|
4880
|
+
taskIds: z82.array(z82.string()).min(1).max(20),
|
|
4881
|
+
requestingUserId: z82.string().optional()
|
|
4882
|
+
});
|
|
4883
|
+
var GetActiveAuditSessionsRequestSchema = z82.object({
|
|
4884
|
+
projectId: z82.string()
|
|
4885
|
+
});
|
|
4886
|
+
var ReportTaskAuditResultRequestSchema = z82.object({
|
|
4887
|
+
projectId: z82.string(),
|
|
4888
|
+
taskId: z82.string(),
|
|
4889
|
+
summary: z82.string(),
|
|
4890
|
+
turnGrades: z82.array(
|
|
4891
|
+
z82.object({
|
|
4892
|
+
turnIndex: z82.number(),
|
|
4893
|
+
phase: z82.enum(["planning", "building", "human"]),
|
|
4894
|
+
grade: z82.enum(["correct", "neutral", "blunder"]),
|
|
4895
|
+
reasoning: z82.string(),
|
|
4896
|
+
eventType: z82.string(),
|
|
4897
|
+
eventSummary: z82.string()
|
|
5083
4898
|
})
|
|
5084
4899
|
),
|
|
5085
|
-
planningAccuracy:
|
|
5086
|
-
buildingAccuracy:
|
|
5087
|
-
humanAccuracy:
|
|
5088
|
-
planningCorrect:
|
|
5089
|
-
planningNeutral:
|
|
5090
|
-
planningBlunder:
|
|
5091
|
-
buildingCorrect:
|
|
5092
|
-
buildingNeutral:
|
|
5093
|
-
buildingBlunder:
|
|
5094
|
-
humanCorrect:
|
|
5095
|
-
humanNeutral:
|
|
5096
|
-
humanBlunder:
|
|
5097
|
-
humanEvaluations:
|
|
5098
|
-
|
|
5099
|
-
messageIndex:
|
|
5100
|
-
rating:
|
|
5101
|
-
reasoning:
|
|
4900
|
+
planningAccuracy: z82.number().nullable(),
|
|
4901
|
+
buildingAccuracy: z82.number().nullable(),
|
|
4902
|
+
humanAccuracy: z82.number().nullable(),
|
|
4903
|
+
planningCorrect: z82.number(),
|
|
4904
|
+
planningNeutral: z82.number(),
|
|
4905
|
+
planningBlunder: z82.number(),
|
|
4906
|
+
buildingCorrect: z82.number(),
|
|
4907
|
+
buildingNeutral: z82.number(),
|
|
4908
|
+
buildingBlunder: z82.number(),
|
|
4909
|
+
humanCorrect: z82.number(),
|
|
4910
|
+
humanNeutral: z82.number(),
|
|
4911
|
+
humanBlunder: z82.number(),
|
|
4912
|
+
humanEvaluations: z82.array(
|
|
4913
|
+
z82.object({
|
|
4914
|
+
messageIndex: z82.number(),
|
|
4915
|
+
rating: z82.union([z82.literal(-1), z82.literal(0), z82.literal(1)]),
|
|
4916
|
+
reasoning: z82.string()
|
|
5102
4917
|
})
|
|
5103
4918
|
).optional(),
|
|
5104
|
-
suggestionIds:
|
|
5105
|
-
auditCostUsd:
|
|
5106
|
-
model:
|
|
4919
|
+
suggestionIds: z82.array(z82.string()),
|
|
4920
|
+
auditCostUsd: z82.number().nullable(),
|
|
4921
|
+
model: z82.string().nullable(),
|
|
5107
4922
|
/** When set, the audit is marked failed with this message instead. */
|
|
5108
|
-
error:
|
|
4923
|
+
error: z82.string().optional()
|
|
5109
4924
|
});
|
|
5110
|
-
var GetTaskAuditsRequestSchema =
|
|
5111
|
-
projectId:
|
|
5112
|
-
limit:
|
|
4925
|
+
var GetTaskAuditsRequestSchema = z82.object({
|
|
4926
|
+
projectId: z82.string(),
|
|
4927
|
+
limit: z82.number().int().positive().max(200).optional().default(50)
|
|
5113
4928
|
});
|
|
5114
|
-
var GetTaskAuditRequestSchema =
|
|
5115
|
-
projectId:
|
|
5116
|
-
auditId:
|
|
4929
|
+
var GetTaskAuditRequestSchema = z82.object({
|
|
4930
|
+
projectId: z82.string(),
|
|
4931
|
+
auditId: z82.string()
|
|
5117
4932
|
});
|
|
5118
|
-
var GetTaskAuditAggregatesRequestSchema =
|
|
5119
|
-
projectId:
|
|
4933
|
+
var GetTaskAuditAggregatesRequestSchema = z82.object({
|
|
4934
|
+
projectId: z82.string()
|
|
5120
4935
|
});
|
|
5121
|
-
var DeleteTaskAuditRequestSchema =
|
|
5122
|
-
projectId:
|
|
5123
|
-
auditId:
|
|
5124
|
-
requestingUserId:
|
|
4936
|
+
var DeleteTaskAuditRequestSchema = z82.object({
|
|
4937
|
+
projectId: z82.string(),
|
|
4938
|
+
auditId: z82.string(),
|
|
4939
|
+
requestingUserId: z82.string().optional()
|
|
5125
4940
|
});
|
|
5126
|
-
var MarkInitialPromptSubmittedRequestSchema =
|
|
5127
|
-
sessionId:
|
|
4941
|
+
var MarkInitialPromptSubmittedRequestSchema = z82.object({
|
|
4942
|
+
sessionId: z82.string()
|
|
5128
4943
|
});
|
|
5129
4944
|
var DECISION_STATUS_LABELS = ["Open", "Decided", "Cancelled"];
|
|
5130
4945
|
var DECISION_OPTION_KINDS = ["current", "planned", "proposed"];
|
|
@@ -5139,166 +4954,272 @@ var DECISION_DEADLINE_MIN_HOURS = 1;
|
|
|
5139
4954
|
var DECISION_DEADLINE_MAX_DAYS = 30;
|
|
5140
4955
|
var DECISION_DEADLINE_MAX_HOURS = DECISION_DEADLINE_MAX_DAYS * 24;
|
|
5141
4956
|
var DECISION_LIST_MAX_LIMIT = 200;
|
|
5142
|
-
var decisionIdSchema =
|
|
5143
|
-
var optionInputSchema =
|
|
5144
|
-
label:
|
|
5145
|
-
description:
|
|
5146
|
-
kind:
|
|
4957
|
+
var decisionIdSchema = z92.string().cuid("Invalid decision ID");
|
|
4958
|
+
var optionInputSchema = z92.object({
|
|
4959
|
+
label: z92.string().min(1, "Option label is required").max(DECISION_OPTION_LABEL_MAX),
|
|
4960
|
+
description: z92.string().min(1, "Describe the option as GIVEN / WHEN / THEN"),
|
|
4961
|
+
kind: z92.enum(DECISION_OPTION_KINDS).optional()
|
|
5147
4962
|
});
|
|
5148
|
-
var optionsArraySchema =
|
|
4963
|
+
var optionsArraySchema = z92.array(optionInputSchema).min(DECISION_OPTIONS_MIN, `A decision needs at least ${DECISION_OPTIONS_MIN} options`).max(
|
|
5149
4964
|
DECISION_OPTIONS_MAX,
|
|
5150
4965
|
`A decision takes at most ${DECISION_OPTIONS_MAX} options \u2014 more than that means the question is not framed yet`
|
|
5151
4966
|
);
|
|
5152
|
-
var CreateDecisionRequestSchema =
|
|
5153
|
-
projectId:
|
|
4967
|
+
var CreateDecisionRequestSchema = z92.object({
|
|
4968
|
+
projectId: z92.string().cuid("Invalid project ID"),
|
|
5154
4969
|
/** The question, as one plain sentence. */
|
|
5155
|
-
title:
|
|
5156
|
-
description:
|
|
5157
|
-
context:
|
|
4970
|
+
title: z92.string().min(1, "A decision needs a question").max(DECISION_QUESTION_MAX),
|
|
4971
|
+
description: z92.string().max(CARD_DESCRIPTION_MAX).optional(),
|
|
4972
|
+
context: z92.string().min(1, "Context is required \u2014 say why this is being asked now"),
|
|
5158
4973
|
options: optionsArraySchema,
|
|
5159
4974
|
/** 1-based position of the option that wins on silence. */
|
|
5160
|
-
defaultOptionOrdinal:
|
|
5161
|
-
deadlineHours:
|
|
4975
|
+
defaultOptionOrdinal: z92.number().int().min(1).max(DECISION_OPTIONS_MAX),
|
|
4976
|
+
deadlineHours: z92.number().min(DECISION_DEADLINE_MIN_HOURS).max(DECISION_DEADLINE_MAX_HOURS).optional(),
|
|
5162
4977
|
/** Cards this decision blocks, by cuid or slug. */
|
|
5163
|
-
impactedCardRefs:
|
|
5164
|
-
tagNames:
|
|
5165
|
-
raisedByTaskId:
|
|
5166
|
-
raisedByPrNumber:
|
|
4978
|
+
impactedCardRefs: z92.array(z92.string().min(1)).max(50).optional(),
|
|
4979
|
+
tagNames: z92.array(z92.string().min(1)).max(10).optional(),
|
|
4980
|
+
raisedByTaskId: z92.string().min(1).optional(),
|
|
4981
|
+
raisedByPrNumber: z92.number().int().positive().optional()
|
|
5167
4982
|
});
|
|
5168
|
-
var VoteDecisionRequestSchema =
|
|
4983
|
+
var VoteDecisionRequestSchema = z92.object({
|
|
5169
4984
|
decisionId: decisionIdSchema,
|
|
5170
|
-
optionId:
|
|
5171
|
-
reason:
|
|
5172
|
-
source:
|
|
4985
|
+
optionId: z92.string().cuid("Invalid option ID"),
|
|
4986
|
+
reason: z92.string().max(DECISION_VOTE_REASON_MAX).optional(),
|
|
4987
|
+
source: z92.enum(DECISION_VOTE_SOURCES).optional()
|
|
5173
4988
|
});
|
|
5174
|
-
var RemoveDecisionVoteRequestSchema =
|
|
4989
|
+
var RemoveDecisionVoteRequestSchema = z92.object({
|
|
5175
4990
|
decisionId: decisionIdSchema
|
|
5176
4991
|
});
|
|
5177
|
-
var ResolveDecisionRequestSchema =
|
|
4992
|
+
var ResolveDecisionRequestSchema = z92.object({
|
|
5178
4993
|
decisionId: decisionIdSchema,
|
|
5179
|
-
chosenOptionId:
|
|
5180
|
-
resolution:
|
|
5181
|
-
resolutionNotes:
|
|
4994
|
+
chosenOptionId: z92.string().cuid("Invalid option ID"),
|
|
4995
|
+
resolution: z92.string().min(1, "A resolution is required \u2014 say what was decided").max(DECISION_RESOLUTION_MAX),
|
|
4996
|
+
resolutionNotes: z92.string().optional()
|
|
5182
4997
|
});
|
|
5183
|
-
var CancelDecisionRequestSchema =
|
|
4998
|
+
var CancelDecisionRequestSchema = z92.object({
|
|
5184
4999
|
decisionId: decisionIdSchema,
|
|
5185
5000
|
/** Why it is being dropped. Posted to the decision thread. */
|
|
5186
|
-
reason:
|
|
5001
|
+
reason: z92.string().max(DECISION_RESOLUTION_MAX).optional()
|
|
5187
5002
|
});
|
|
5188
|
-
var BulkDecisionIdsRequestSchema =
|
|
5189
|
-
projectId:
|
|
5190
|
-
decisionIds:
|
|
5003
|
+
var BulkDecisionIdsRequestSchema = z92.object({
|
|
5004
|
+
projectId: z92.string().cuid("Invalid project ID"),
|
|
5005
|
+
decisionIds: z92.array(z92.string().cuid()).min(1).max(200)
|
|
5191
5006
|
});
|
|
5192
|
-
var ReopenDecisionRequestSchema =
|
|
5007
|
+
var ReopenDecisionRequestSchema = z92.object({
|
|
5193
5008
|
decisionId: decisionIdSchema,
|
|
5194
|
-
deadlineHours:
|
|
5009
|
+
deadlineHours: z92.number().min(DECISION_DEADLINE_MIN_HOURS).max(DECISION_DEADLINE_MAX_HOURS).optional()
|
|
5195
5010
|
});
|
|
5196
|
-
var UpdateDecisionRequestSchema =
|
|
5011
|
+
var UpdateDecisionRequestSchema = z92.object({
|
|
5197
5012
|
decisionId: decisionIdSchema,
|
|
5198
|
-
title:
|
|
5199
|
-
description:
|
|
5200
|
-
context:
|
|
5201
|
-
deadlineAt:
|
|
5013
|
+
title: z92.string().min(1).max(200).optional(),
|
|
5014
|
+
description: z92.string().max(CARD_DESCRIPTION_MAX).optional(),
|
|
5015
|
+
context: z92.string().min(1).optional(),
|
|
5016
|
+
deadlineAt: z92.string().datetime().optional(),
|
|
5202
5017
|
/** Only accepted while the decision has zero votes. */
|
|
5203
5018
|
options: optionsArraySchema.optional(),
|
|
5204
|
-
defaultOptionOrdinal:
|
|
5205
|
-
addImpactedCardRefs:
|
|
5206
|
-
removeImpactedCardIds:
|
|
5019
|
+
defaultOptionOrdinal: z92.number().int().min(1).max(DECISION_OPTIONS_MAX).optional(),
|
|
5020
|
+
addImpactedCardRefs: z92.array(z92.string().min(1)).max(50).optional(),
|
|
5021
|
+
removeImpactedCardIds: z92.array(z92.string().min(1)).max(50).optional()
|
|
5207
5022
|
});
|
|
5208
|
-
var GetDecisionRequestSchema =
|
|
5023
|
+
var GetDecisionRequestSchema = z92.object({
|
|
5209
5024
|
/** cuid or slug. A slug needs `projectId` to resolve unambiguously. */
|
|
5210
|
-
decisionId:
|
|
5211
|
-
projectId:
|
|
5025
|
+
decisionId: z92.string().min(1),
|
|
5026
|
+
projectId: z92.string().cuid().optional()
|
|
5212
5027
|
});
|
|
5213
|
-
var ListDecisionsRequestSchema =
|
|
5214
|
-
projectId:
|
|
5215
|
-
status:
|
|
5028
|
+
var ListDecisionsRequestSchema = z92.object({
|
|
5029
|
+
projectId: z92.string().cuid("Invalid project ID"),
|
|
5030
|
+
status: z92.enum(DECISION_STATUS_LABELS).optional(),
|
|
5216
5031
|
/** Tag name, not id — an agent knows the tag it is working under. */
|
|
5217
|
-
tag:
|
|
5032
|
+
tag: z92.string().min(1).optional(),
|
|
5218
5033
|
/** Decisions that this card is blocked by (cuid or slug). */
|
|
5219
|
-
cardId:
|
|
5220
|
-
query:
|
|
5221
|
-
limit:
|
|
5222
|
-
});
|
|
5223
|
-
var optionInput =
|
|
5224
|
-
label:
|
|
5225
|
-
description:
|
|
5226
|
-
kind:
|
|
5227
|
-
});
|
|
5228
|
-
var optionsInput =
|
|
5229
|
-
var deadlineHours =
|
|
5230
|
-
var CreateProjectDecisionRequestSchema =
|
|
5231
|
-
projectId:
|
|
5232
|
-
question:
|
|
5233
|
-
summary:
|
|
5234
|
-
context:
|
|
5034
|
+
cardId: z92.string().min(1).optional(),
|
|
5035
|
+
query: z92.string().min(1).optional(),
|
|
5036
|
+
limit: z92.number().int().min(1).max(DECISION_LIST_MAX_LIMIT).optional()
|
|
5037
|
+
});
|
|
5038
|
+
var optionInput = z10.object({
|
|
5039
|
+
label: z10.string().min(1).max(DECISION_OPTION_LABEL_MAX),
|
|
5040
|
+
description: z10.string().min(1),
|
|
5041
|
+
kind: z10.enum(DECISION_OPTION_KINDS).optional()
|
|
5042
|
+
});
|
|
5043
|
+
var optionsInput = z10.array(optionInput).min(DECISION_OPTIONS_MIN).max(DECISION_OPTIONS_MAX);
|
|
5044
|
+
var deadlineHours = z10.number().min(DECISION_DEADLINE_MIN_HOURS).max(DECISION_DEADLINE_MAX_HOURS).optional();
|
|
5045
|
+
var CreateProjectDecisionRequestSchema = z10.object({
|
|
5046
|
+
projectId: z10.string(),
|
|
5047
|
+
question: z10.string().min(1).max(200),
|
|
5048
|
+
summary: z10.string().max(CARD_DESCRIPTION_MAX).optional(),
|
|
5049
|
+
context: z10.string().min(1),
|
|
5235
5050
|
options: optionsInput,
|
|
5236
|
-
defaultOption:
|
|
5051
|
+
defaultOption: z10.number().int().min(1).max(DECISION_OPTIONS_MAX),
|
|
5237
5052
|
deadlineHours,
|
|
5238
|
-
impactedCards:
|
|
5053
|
+
impactedCards: z10.array(z10.string().min(1)).max(50).optional(),
|
|
5239
5054
|
/**
|
|
5240
5055
|
* Pod surface only: also block the card this session is working on. The
|
|
5241
5056
|
* session's task id is resolved SERVER-side from `sessionId` — a
|
|
5242
5057
|
* client-supplied card id would let a pod block any card in the project.
|
|
5243
5058
|
*/
|
|
5244
|
-
includeCurrentCard:
|
|
5245
|
-
sessionId:
|
|
5246
|
-
tagNames:
|
|
5247
|
-
raisedByPr:
|
|
5059
|
+
includeCurrentCard: z10.boolean().optional(),
|
|
5060
|
+
sessionId: z10.string().optional(),
|
|
5061
|
+
tagNames: z10.array(z10.string().min(1)).max(10).optional(),
|
|
5062
|
+
raisedByPr: z10.number().int().positive().optional(),
|
|
5248
5063
|
/** Slug or id of the card that raised the question. A link back, not a block. */
|
|
5249
|
-
raisedByCard:
|
|
5064
|
+
raisedByCard: z10.string().min(1).optional(),
|
|
5250
5065
|
/** Absolute deadline (ISO 8601). Wins over `deadlineHours` when both are sent. */
|
|
5251
|
-
deadlineAt:
|
|
5252
|
-
requestingUserId:
|
|
5253
|
-
});
|
|
5254
|
-
var ListProjectDecisionsRequestSchema =
|
|
5255
|
-
projectId:
|
|
5256
|
-
status:
|
|
5257
|
-
tag:
|
|
5258
|
-
card:
|
|
5259
|
-
query:
|
|
5260
|
-
limit:
|
|
5261
|
-
requestingUserId:
|
|
5262
|
-
});
|
|
5263
|
-
var GetProjectDecisionRequestSchema =
|
|
5264
|
-
projectId:
|
|
5265
|
-
decision:
|
|
5266
|
-
requestingUserId:
|
|
5267
|
-
});
|
|
5268
|
-
var VoteProjectDecisionRequestSchema =
|
|
5269
|
-
projectId:
|
|
5270
|
-
decision:
|
|
5066
|
+
deadlineAt: z10.string().datetime({ offset: true }).optional(),
|
|
5067
|
+
requestingUserId: z10.string().optional()
|
|
5068
|
+
});
|
|
5069
|
+
var ListProjectDecisionsRequestSchema = z10.object({
|
|
5070
|
+
projectId: z10.string(),
|
|
5071
|
+
status: z10.enum(DECISION_STATUS_LABELS).optional(),
|
|
5072
|
+
tag: z10.string().min(1).optional(),
|
|
5073
|
+
card: z10.string().min(1).optional(),
|
|
5074
|
+
query: z10.string().min(1).optional(),
|
|
5075
|
+
limit: z10.number().int().min(1).max(DECISION_LIST_MAX_LIMIT).optional(),
|
|
5076
|
+
requestingUserId: z10.string().optional()
|
|
5077
|
+
});
|
|
5078
|
+
var GetProjectDecisionRequestSchema = z10.object({
|
|
5079
|
+
projectId: z10.string(),
|
|
5080
|
+
decision: z10.string().min(1),
|
|
5081
|
+
requestingUserId: z10.string().optional()
|
|
5082
|
+
});
|
|
5083
|
+
var VoteProjectDecisionRequestSchema = z10.object({
|
|
5084
|
+
projectId: z10.string(),
|
|
5085
|
+
decision: z10.string().min(1),
|
|
5271
5086
|
/** An option id, or its 1-based position. */
|
|
5272
|
-
option:
|
|
5273
|
-
reason:
|
|
5274
|
-
requestingUserId:
|
|
5275
|
-
});
|
|
5276
|
-
var ResolveProjectDecisionRequestSchema =
|
|
5277
|
-
projectId:
|
|
5278
|
-
decision:
|
|
5279
|
-
option:
|
|
5280
|
-
resolution:
|
|
5281
|
-
notes:
|
|
5282
|
-
requestingUserId:
|
|
5283
|
-
});
|
|
5284
|
-
var UpdateProjectDecisionRequestSchema =
|
|
5285
|
-
projectId:
|
|
5286
|
-
decision:
|
|
5287
|
-
question:
|
|
5288
|
-
summary:
|
|
5289
|
-
context:
|
|
5087
|
+
option: z10.string().min(1),
|
|
5088
|
+
reason: z10.string().max(CARD_DESCRIPTION_MAX).optional(),
|
|
5089
|
+
requestingUserId: z10.string().optional()
|
|
5090
|
+
});
|
|
5091
|
+
var ResolveProjectDecisionRequestSchema = z10.object({
|
|
5092
|
+
projectId: z10.string(),
|
|
5093
|
+
decision: z10.string().min(1),
|
|
5094
|
+
option: z10.string().min(1),
|
|
5095
|
+
resolution: z10.string().min(1).max(DECISION_RESOLUTION_MAX),
|
|
5096
|
+
notes: z10.string().optional(),
|
|
5097
|
+
requestingUserId: z10.string().optional()
|
|
5098
|
+
});
|
|
5099
|
+
var UpdateProjectDecisionRequestSchema = z10.object({
|
|
5100
|
+
projectId: z10.string(),
|
|
5101
|
+
decision: z10.string().min(1),
|
|
5102
|
+
question: z10.string().min(1).max(200).optional(),
|
|
5103
|
+
summary: z10.string().max(CARD_DESCRIPTION_MAX).optional(),
|
|
5104
|
+
context: z10.string().min(1).optional(),
|
|
5290
5105
|
/** Accepted only while nobody has voted. */
|
|
5291
5106
|
options: optionsInput.optional(),
|
|
5292
|
-
defaultOption:
|
|
5293
|
-
tagNames:
|
|
5107
|
+
defaultOption: z10.number().int().min(1).max(DECISION_OPTIONS_MAX).optional(),
|
|
5108
|
+
tagNames: z10.array(z10.string().min(1)).max(10).optional(),
|
|
5294
5109
|
deadlineHours,
|
|
5295
5110
|
/** Absolute deadline (ISO 8601). Wins over `deadlineHours` when both are sent. */
|
|
5296
|
-
deadlineAt:
|
|
5297
|
-
addImpactedCards:
|
|
5298
|
-
removeImpactedCards:
|
|
5299
|
-
status:
|
|
5300
|
-
requestingUserId:
|
|
5111
|
+
deadlineAt: z10.string().datetime({ offset: true }).optional(),
|
|
5112
|
+
addImpactedCards: z10.array(z10.string().min(1)).max(50).optional(),
|
|
5113
|
+
removeImpactedCards: z10.array(z10.string().min(1)).max(50).optional(),
|
|
5114
|
+
status: z10.enum(["Cancelled", "Open"]).optional(),
|
|
5115
|
+
requestingUserId: z10.string().optional()
|
|
5116
|
+
});
|
|
5117
|
+
function formatModelId(provider, model) {
|
|
5118
|
+
return `${provider}/${model}`;
|
|
5119
|
+
}
|
|
5120
|
+
var JEV_MODEL = "jev-1.13.0";
|
|
5121
|
+
var JEV_MODEL_ID = formatModelId("typesafe", JEV_MODEL);
|
|
5122
|
+
var JEV_INPUT_PRICE_PER_TOKEN = 0.042 / 1e6;
|
|
5123
|
+
var JEV_STATE_MAX_CHARS = 1e5;
|
|
5124
|
+
var EVALUATE_MAX_QUESTIONS = 32;
|
|
5125
|
+
var EVALUATE_MAX_ITEMS = 100;
|
|
5126
|
+
var EVALUATE_ITEM_MAX_CHARS = 8e3;
|
|
5127
|
+
var EVALUATE_REQUEST_MAX_CHARS = 4e5;
|
|
5128
|
+
var TEXT_MAX = 1e3;
|
|
5129
|
+
var QUESTION_KEY = /^[A-Za-z0-9_-]{1,64}$/;
|
|
5130
|
+
var EvaluateQuestionSchema = z11.object({
|
|
5131
|
+
key: z11.string().regex(QUESTION_KEY, "key must be 1-64 letters, digits, _ or -"),
|
|
5132
|
+
type: z11.enum(["noul", "choice", "score"]),
|
|
5133
|
+
instructions: z11.string().min(1).max(4e3),
|
|
5134
|
+
options: z11.array(
|
|
5135
|
+
z11.object({
|
|
5136
|
+
key: z11.string().min(1).max(200),
|
|
5137
|
+
description: z11.string().min(1).max(TEXT_MAX)
|
|
5138
|
+
})
|
|
5139
|
+
).optional(),
|
|
5140
|
+
levels: z11.array(z11.string().min(1).max(TEXT_MAX)).optional()
|
|
5141
|
+
}).superRefine((q, ctx) => {
|
|
5142
|
+
if (q.type === "choice") {
|
|
5143
|
+
const n = q.options?.length ?? 0;
|
|
5144
|
+
if (n < 2 || n > 255) {
|
|
5145
|
+
ctx.addIssue({
|
|
5146
|
+
code: z11.ZodIssueCode.custom,
|
|
5147
|
+
path: ["options"],
|
|
5148
|
+
message: "a choice question needs 2-255 options"
|
|
5149
|
+
});
|
|
5150
|
+
} else if (new Set(q.options?.map((o) => o.key)).size !== n) {
|
|
5151
|
+
ctx.addIssue({
|
|
5152
|
+
code: z11.ZodIssueCode.custom,
|
|
5153
|
+
path: ["options"],
|
|
5154
|
+
message: "option keys must be unique"
|
|
5155
|
+
});
|
|
5156
|
+
}
|
|
5157
|
+
}
|
|
5158
|
+
if (q.type === "score") {
|
|
5159
|
+
const n = q.levels?.length ?? 0;
|
|
5160
|
+
if (n < 2 || n > 10) {
|
|
5161
|
+
ctx.addIssue({
|
|
5162
|
+
code: z11.ZodIssueCode.custom,
|
|
5163
|
+
path: ["levels"],
|
|
5164
|
+
message: "a score question needs 2-10 levels"
|
|
5165
|
+
});
|
|
5166
|
+
}
|
|
5167
|
+
}
|
|
5168
|
+
});
|
|
5169
|
+
var EvaluateQuestionsRequestSchema = z11.object({
|
|
5170
|
+
projectId: z11.string(),
|
|
5171
|
+
state: z11.string().max(JEV_STATE_MAX_CHARS).optional(),
|
|
5172
|
+
items: z11.array(z11.string().max(EVALUATE_ITEM_MAX_CHARS)).max(EVALUATE_MAX_ITEMS).optional(),
|
|
5173
|
+
questions: z11.array(EvaluateQuestionSchema).min(1).max(EVALUATE_MAX_QUESTIONS)
|
|
5174
|
+
}).superRefine((req, ctx) => {
|
|
5175
|
+
if (req.state === void 0 && (!req.items || req.items.length === 0)) {
|
|
5176
|
+
ctx.addIssue({
|
|
5177
|
+
code: z11.ZodIssueCode.custom,
|
|
5178
|
+
path: ["state"],
|
|
5179
|
+
message: "pass state, or a non-empty items array"
|
|
5180
|
+
});
|
|
5181
|
+
}
|
|
5182
|
+
if (JSON.stringify(req).length > EVALUATE_REQUEST_MAX_CHARS) {
|
|
5183
|
+
ctx.addIssue({
|
|
5184
|
+
code: z11.ZodIssueCode.custom,
|
|
5185
|
+
path: [],
|
|
5186
|
+
message: `the request is over ${EVALUATE_REQUEST_MAX_CHARS} characters; split the items across calls`
|
|
5187
|
+
});
|
|
5188
|
+
}
|
|
5189
|
+
if (new Set(req.questions.map((q) => q.key)).size !== req.questions.length) {
|
|
5190
|
+
ctx.addIssue({
|
|
5191
|
+
code: z11.ZodIssueCode.custom,
|
|
5192
|
+
path: ["questions"],
|
|
5193
|
+
message: "question keys must be unique"
|
|
5194
|
+
});
|
|
5195
|
+
}
|
|
5301
5196
|
});
|
|
5197
|
+
var round3 = (n) => Math.round(n * 1e3) / 1e3;
|
|
5198
|
+
function compactAnswer(answer) {
|
|
5199
|
+
switch (answer.type) {
|
|
5200
|
+
case "noul":
|
|
5201
|
+
return round3(answer.noul);
|
|
5202
|
+
case "choice":
|
|
5203
|
+
return {
|
|
5204
|
+
choice: answer.choice,
|
|
5205
|
+
p: round3(answer.probabilities[answer.choice] ?? 0),
|
|
5206
|
+
confidence: round3(answer.confidence)
|
|
5207
|
+
};
|
|
5208
|
+
case "score":
|
|
5209
|
+
return { score: answer.score, confidence: round3(answer.confidence) };
|
|
5210
|
+
}
|
|
5211
|
+
}
|
|
5212
|
+
function formatEvaluateQuestionsResult(res) {
|
|
5213
|
+
const results = res.results.map(
|
|
5214
|
+
(r) => "error" in r ? { index: r.index, error: r.error } : {
|
|
5215
|
+
index: r.index,
|
|
5216
|
+
answers: Object.fromEntries(
|
|
5217
|
+
Object.entries(r.answers).map(([key, a]) => [key, compactAnswer(a)])
|
|
5218
|
+
)
|
|
5219
|
+
}
|
|
5220
|
+
);
|
|
5221
|
+
return JSON.stringify({ model: res.model, results });
|
|
5222
|
+
}
|
|
5302
5223
|
var MEETING_CHECKLIST_TITLE_MAX = 300;
|
|
5303
5224
|
var MEETING_TRANSCRIPT_MAX_CHARS = 2e6;
|
|
5304
5225
|
var MEETING_TITLE_MAX = 200;
|
|
@@ -5435,9 +5356,6 @@ var UpdateProjectMeetingRequestSchema = z12.object({
|
|
|
5435
5356
|
var TASK_CHAT_HISTORY_LIMIT = 20;
|
|
5436
5357
|
var PM_CHAT_HISTORY_LIMIT = 40;
|
|
5437
5358
|
var AGENT_CHAT_HISTORY_FETCH_LIMIT = Math.max(TASK_CHAT_HISTORY_LIMIT, PM_CHAT_HISTORY_LIMIT) + 10;
|
|
5438
|
-
function formatModelId(provider, model) {
|
|
5439
|
-
return `${provider}/${model}`;
|
|
5440
|
-
}
|
|
5441
5359
|
function anthropicEntry(model, label, inputPerMillion, outputPerMillion, opts = {}) {
|
|
5442
5360
|
return {
|
|
5443
5361
|
provider: "anthropic",
|
|
@@ -5776,26 +5694,371 @@ var BOARD_CARD_TYPES = surfaceTypes("board");
|
|
|
5776
5694
|
var REPORT_CARD_TYPES = surfaceTypes("report");
|
|
5777
5695
|
var DECISION_CARD_TYPES = surfaceTypes("decision");
|
|
5778
5696
|
|
|
5779
|
-
// src/tools/
|
|
5780
|
-
function
|
|
5781
|
-
|
|
5782
|
-
|
|
5783
|
-
|
|
5784
|
-
|
|
5785
|
-
|
|
5786
|
-
|
|
5787
|
-
|
|
5788
|
-
|
|
5697
|
+
// src/tools/checklists.ts
|
|
5698
|
+
function renderManualTestGroups(groups) {
|
|
5699
|
+
const lines = [];
|
|
5700
|
+
for (const g of groups) {
|
|
5701
|
+
lines.push(`## ${g.title} [${g.status}] (${g.slug})`);
|
|
5702
|
+
for (const t of g.tests) {
|
|
5703
|
+
const mark = t.status === "approved" ? "\u2713" : t.status === "rejected" ? "\u2717" : "\u25CB";
|
|
5704
|
+
lines.push(` ${mark} ${t.title} \u2014 ${t.status}`);
|
|
5705
|
+
if (t.status === "rejected") {
|
|
5706
|
+
for (const f2 of t.failures ?? []) {
|
|
5707
|
+
lines.push(` \u26A0 ${f2.userName ?? "Someone"}: ${f2.reason ?? "(no message)"}`);
|
|
5708
|
+
}
|
|
5709
|
+
}
|
|
5710
|
+
}
|
|
5711
|
+
lines.push("");
|
|
5712
|
+
}
|
|
5713
|
+
return lines.join("\n").trimEnd();
|
|
5789
5714
|
}
|
|
5790
|
-
|
|
5791
|
-
|
|
5792
|
-
|
|
5793
|
-
|
|
5794
|
-
|
|
5795
|
-
|
|
5796
|
-
|
|
5797
|
-
|
|
5798
|
-
|
|
5715
|
+
function registerListManualTests(server2, conn2) {
|
|
5716
|
+
registerContractTool(server2, listManualTestsContract, async (params) => {
|
|
5717
|
+
const items = await conn2.listManualTests(params.taskId, params.projectId);
|
|
5718
|
+
if (items.length === 0) {
|
|
5719
|
+
return { content: [{ type: "text", text: "No manual tests recorded for this task." }] };
|
|
5720
|
+
}
|
|
5721
|
+
const lines = items.flatMap((item, i) => {
|
|
5722
|
+
const checked = item.checked ? "[x]" : "[ ]";
|
|
5723
|
+
const row = [`${i + 1}. ${checked} ${item.title}`];
|
|
5724
|
+
for (const f2 of item.failures ?? []) {
|
|
5725
|
+
const who = f2.userName ?? "Someone";
|
|
5726
|
+
const reason = f2.reason ?? "(no message)";
|
|
5727
|
+
row.push(` \u26A0 Failed (${who}): ${reason}`);
|
|
5728
|
+
}
|
|
5729
|
+
return row;
|
|
5730
|
+
});
|
|
5731
|
+
return { content: [{ type: "text", text: lines.join("\n") }] };
|
|
5732
|
+
});
|
|
5733
|
+
}
|
|
5734
|
+
function registerSetManualTests(server2, conn2) {
|
|
5735
|
+
registerContractTool(server2, setManualTestsContract, async (params) => {
|
|
5736
|
+
const result = await conn2.setManualTests(params.taskId, params.items, params.projectId);
|
|
5737
|
+
const parts = [`Created ${result.created} manual test item(s).`];
|
|
5738
|
+
if (result.skipped > 0) parts.push(`Skipped ${result.skipped} duplicate(s).`);
|
|
5739
|
+
for (const item of result.rejected ?? []) parts.push(describeManualTestRejection(item));
|
|
5740
|
+
return { content: [{ type: "text", text: parts.join(" ") }] };
|
|
5741
|
+
});
|
|
5742
|
+
}
|
|
5743
|
+
function registerEditManualTest(server2, conn2) {
|
|
5744
|
+
registerContractTool(server2, editManualTestContract, async (params) => {
|
|
5745
|
+
await conn2.editManualTest(params.taskId, params.title, params.newTitle, params.projectId);
|
|
5746
|
+
return { content: [{ type: "text", text: `Updated manual test to "${params.newTitle}".` }] };
|
|
5747
|
+
});
|
|
5748
|
+
}
|
|
5749
|
+
function registerRemoveManualTest(server2, conn2) {
|
|
5750
|
+
registerContractTool(server2, removeManualTestContract, async (params) => {
|
|
5751
|
+
await conn2.removeManualTest(params.taskId, params.title, params.projectId);
|
|
5752
|
+
return { content: [{ type: "text", text: `Removed manual test "${params.title}".` }] };
|
|
5753
|
+
});
|
|
5754
|
+
}
|
|
5755
|
+
function registerApproveManualTest(server2, conn2) {
|
|
5756
|
+
registerContractTool(server2, approveManualTestContract, async (params) => {
|
|
5757
|
+
await conn2.approveManualTest(params.taskId, params.title, params.projectId);
|
|
5758
|
+
return { content: [{ type: "text", text: `Approved manual test "${params.title}".` }] };
|
|
5759
|
+
});
|
|
5760
|
+
}
|
|
5761
|
+
function registerRejectManualTest(server2, conn2) {
|
|
5762
|
+
registerContractTool(server2, rejectManualTestContract, async (params) => {
|
|
5763
|
+
await conn2.rejectManualTest(params.taskId, params.title, params.reason, params.projectId);
|
|
5764
|
+
return {
|
|
5765
|
+
content: [
|
|
5766
|
+
{
|
|
5767
|
+
type: "text",
|
|
5768
|
+
text: `Flagged an issue with manual test "${params.title}": ${params.reason}`
|
|
5769
|
+
}
|
|
5770
|
+
]
|
|
5771
|
+
};
|
|
5772
|
+
});
|
|
5773
|
+
}
|
|
5774
|
+
function registerQueryManualTests(server2, conn2) {
|
|
5775
|
+
registerContractTool(server2, queryManualTestsContract, async (params) => {
|
|
5776
|
+
const groups = await conn2.queryManualTests({
|
|
5777
|
+
projectId: params.projectId,
|
|
5778
|
+
cardStatuses: params.cardStatuses,
|
|
5779
|
+
testStatuses: params.testStatuses
|
|
5780
|
+
});
|
|
5781
|
+
if (groups.length === 0) {
|
|
5782
|
+
return { content: [{ type: "text", text: "No manual tests match those filters." }] };
|
|
5783
|
+
}
|
|
5784
|
+
return { content: [{ type: "text", text: renderManualTestGroups(groups) }] };
|
|
5785
|
+
});
|
|
5786
|
+
}
|
|
5787
|
+
function registerChecklistTools(server2, conn2) {
|
|
5788
|
+
registerListManualTests(server2, conn2);
|
|
5789
|
+
registerQueryManualTests(server2, conn2);
|
|
5790
|
+
registerSetManualTests(server2, conn2);
|
|
5791
|
+
registerEditManualTest(server2, conn2);
|
|
5792
|
+
registerRemoveManualTest(server2, conn2);
|
|
5793
|
+
registerApproveManualTest(server2, conn2);
|
|
5794
|
+
registerRejectManualTest(server2, conn2);
|
|
5795
|
+
}
|
|
5796
|
+
|
|
5797
|
+
// src/tools/workspace.ts
|
|
5798
|
+
import { z as z13 } from "zod";
|
|
5799
|
+
|
|
5800
|
+
// src/workspace-ssh-tunnel.ts
|
|
5801
|
+
import net from "net";
|
|
5802
|
+
import { once } from "events";
|
|
5803
|
+
import { WebSocket } from "ws";
|
|
5804
|
+
var WORKSPACE_TUNNEL_HEARTBEAT_MS = 3e4;
|
|
5805
|
+
function withPort(url, port) {
|
|
5806
|
+
const parsed = new URL(url);
|
|
5807
|
+
parsed.searchParams.set("port", String(port));
|
|
5808
|
+
return parsed.toString();
|
|
5809
|
+
}
|
|
5810
|
+
function toBuffer(data) {
|
|
5811
|
+
if (Buffer.isBuffer(data)) return data;
|
|
5812
|
+
if (data instanceof ArrayBuffer) return Buffer.from(data);
|
|
5813
|
+
return Buffer.concat(data);
|
|
5814
|
+
}
|
|
5815
|
+
function startHeartbeat(ws) {
|
|
5816
|
+
const timer = setInterval(() => {
|
|
5817
|
+
if (ws.readyState === WebSocket.OPEN) ws.ping();
|
|
5818
|
+
}, WORKSPACE_TUNNEL_HEARTBEAT_MS);
|
|
5819
|
+
timer.unref?.();
|
|
5820
|
+
return () => clearInterval(timer);
|
|
5821
|
+
}
|
|
5822
|
+
function bridgeClient(client, options) {
|
|
5823
|
+
let ws = null;
|
|
5824
|
+
let ready = false;
|
|
5825
|
+
const pending = [];
|
|
5826
|
+
client.on("data", (data) => {
|
|
5827
|
+
const chunk = Buffer.isBuffer(data) ? data : Buffer.from(data);
|
|
5828
|
+
if (ready && ws && ws.readyState === WebSocket.OPEN) ws.send(chunk);
|
|
5829
|
+
else pending.push(chunk);
|
|
5830
|
+
});
|
|
5831
|
+
client.on("close", () => ws?.close());
|
|
5832
|
+
client.on("error", () => ws?.close());
|
|
5833
|
+
options.resolveTarget().then(({ tunnelUrl, attachToken }) => {
|
|
5834
|
+
const socket = new WebSocket(withPort(tunnelUrl, options.remotePort), {
|
|
5835
|
+
headers: { "x-workspace-attach-token": attachToken }
|
|
5836
|
+
});
|
|
5837
|
+
ws = socket;
|
|
5838
|
+
const stopHeartbeat = startHeartbeat(socket);
|
|
5839
|
+
socket.on("message", (data) => {
|
|
5840
|
+
const chunk = toBuffer(data);
|
|
5841
|
+
if (!ready) {
|
|
5842
|
+
const text = chunk.toString("utf8");
|
|
5843
|
+
if (text === JSON.stringify({ ok: true })) {
|
|
5844
|
+
ready = true;
|
|
5845
|
+
for (const buffered of pending.splice(0)) socket.send(buffered);
|
|
5846
|
+
return;
|
|
5847
|
+
}
|
|
5848
|
+
client.destroy(new Error(`Workspace tunnel rejected connection: ${text}`));
|
|
5849
|
+
socket.close();
|
|
5850
|
+
return;
|
|
5851
|
+
}
|
|
5852
|
+
client.write(chunk);
|
|
5853
|
+
});
|
|
5854
|
+
socket.on("close", () => {
|
|
5855
|
+
stopHeartbeat();
|
|
5856
|
+
client.destroy();
|
|
5857
|
+
});
|
|
5858
|
+
socket.on("error", (err) => {
|
|
5859
|
+
stopHeartbeat();
|
|
5860
|
+
client.destroy(err);
|
|
5861
|
+
});
|
|
5862
|
+
}).catch((err) => {
|
|
5863
|
+
client.destroy(err instanceof Error ? err : new Error(String(err)));
|
|
5864
|
+
});
|
|
5865
|
+
}
|
|
5866
|
+
async function startWorkspaceSshTunnel(options) {
|
|
5867
|
+
const host = options.host ?? "127.0.0.1";
|
|
5868
|
+
const server2 = net.createServer((client) => bridgeClient(client, options));
|
|
5869
|
+
const ac = new AbortController();
|
|
5870
|
+
server2.listen(options.preferredLocalPort ?? 0, host);
|
|
5871
|
+
try {
|
|
5872
|
+
await Promise.race([
|
|
5873
|
+
once(server2, "listening", { signal: ac.signal }),
|
|
5874
|
+
once(server2, "error", { signal: ac.signal }).then(([err]) => {
|
|
5875
|
+
throw err instanceof Error ? err : new Error(String(err));
|
|
5876
|
+
})
|
|
5877
|
+
]);
|
|
5878
|
+
} catch (err) {
|
|
5879
|
+
server2.close();
|
|
5880
|
+
throw new Error(
|
|
5881
|
+
`Failed to bind workspace SSH tunnel: ${err instanceof Error ? err.message : String(err)}`,
|
|
5882
|
+
{ cause: err }
|
|
5883
|
+
);
|
|
5884
|
+
} finally {
|
|
5885
|
+
ac.abort();
|
|
5886
|
+
}
|
|
5887
|
+
const address = server2.address();
|
|
5888
|
+
if (!address || typeof address === "string") {
|
|
5889
|
+
server2.close();
|
|
5890
|
+
throw new Error("Failed to bind workspace SSH tunnel");
|
|
5891
|
+
}
|
|
5892
|
+
return {
|
|
5893
|
+
host,
|
|
5894
|
+
port: address.port,
|
|
5895
|
+
close: () => new Promise((resolve, reject) => {
|
|
5896
|
+
server2.close((err) => {
|
|
5897
|
+
if (err) reject(err);
|
|
5898
|
+
else resolve();
|
|
5899
|
+
});
|
|
5900
|
+
})
|
|
5901
|
+
};
|
|
5902
|
+
}
|
|
5903
|
+
|
|
5904
|
+
// src/tools/workspace.ts
|
|
5905
|
+
var activeTunnels = /* @__PURE__ */ new Map();
|
|
5906
|
+
function createTunnelId(taskId, port) {
|
|
5907
|
+
return `${taskId}:${port}:${Date.now().toString(36)}:${Math.random().toString(36).slice(2, 8)}`;
|
|
5908
|
+
}
|
|
5909
|
+
function localUrlFor(port, host, localPort) {
|
|
5910
|
+
if (port === 2222) return void 0;
|
|
5911
|
+
return `http://${host}:${localPort}`;
|
|
5912
|
+
}
|
|
5913
|
+
function registerAttachInfoTool(server2, conn2) {
|
|
5914
|
+
server2.tool(
|
|
5915
|
+
"workspace_attach_info",
|
|
5916
|
+
"Return SSH/SFTP attach metadata for a running task Claudespace, plus hosted preview URLs and configured preview ports. Optionally installs an OpenSSH public key for this attach session.",
|
|
5917
|
+
{
|
|
5918
|
+
taskId: z13.string().describe("The task ID"),
|
|
5919
|
+
sshPublicKey: z13.string().optional().describe("Optional OpenSSH public key to install into the workspace")
|
|
5920
|
+
},
|
|
5921
|
+
async ({ taskId, sshPublicKey }) => {
|
|
5922
|
+
const info = await conn2.getWorkspaceAttachInfo(taskId, sshPublicKey);
|
|
5923
|
+
return { content: [{ type: "text", text: JSON.stringify(info, null, 2) }] };
|
|
5924
|
+
}
|
|
5925
|
+
);
|
|
5926
|
+
}
|
|
5927
|
+
function registerPreviewUrlsTool(server2, conn2) {
|
|
5928
|
+
server2.tool(
|
|
5929
|
+
"workspace_preview_urls",
|
|
5930
|
+
"Return the hosted preview URLs and preview ports for a running task Claudespace. This mirrors the web UI preview link metadata.",
|
|
5931
|
+
{
|
|
5932
|
+
taskId: z13.string().describe("The task ID")
|
|
5933
|
+
},
|
|
5934
|
+
async ({ taskId }) => {
|
|
5935
|
+
const info = await conn2.getWorkspaceAttachInfo(taskId);
|
|
5936
|
+
return {
|
|
5937
|
+
content: [
|
|
5938
|
+
{
|
|
5939
|
+
type: "text",
|
|
5940
|
+
text: JSON.stringify(
|
|
5941
|
+
{
|
|
5942
|
+
taskId: info.taskId,
|
|
5943
|
+
sessionId: info.sessionId,
|
|
5944
|
+
previewPorts: info.previewPorts,
|
|
5945
|
+
previewUrls: info.previewUrls
|
|
5946
|
+
},
|
|
5947
|
+
null,
|
|
5948
|
+
2
|
|
5949
|
+
)
|
|
5950
|
+
}
|
|
5951
|
+
]
|
|
5952
|
+
};
|
|
5953
|
+
}
|
|
5954
|
+
);
|
|
5955
|
+
}
|
|
5956
|
+
function registerStartTunnelTool(server2, conn2, startTunnel) {
|
|
5957
|
+
server2.tool(
|
|
5958
|
+
"workspace_start_tunnel",
|
|
5959
|
+
"Start a local loopback tunnel through the MCP server to a running task Claudespace port. Use port 2222 for SSH/SFTP, or one of previewPorts for app access.",
|
|
5960
|
+
{
|
|
5961
|
+
taskId: z13.string().describe("The task ID"),
|
|
5962
|
+
port: z13.number().optional().describe("Remote Claudespace port. Defaults to SSH port 2222."),
|
|
5963
|
+
preferredLocalPort: z13.number().optional().describe("Preferred local loopback port. If omitted, the OS chooses one."),
|
|
5964
|
+
sshPublicKey: z13.string().optional().describe("Optional OpenSSH public key to install before opening the tunnel")
|
|
5965
|
+
},
|
|
5966
|
+
async ({ taskId, port, preferredLocalPort, sshPublicKey }) => {
|
|
5967
|
+
const info = await conn2.getWorkspaceAttachInfo(taskId, sshPublicKey);
|
|
5968
|
+
if (!info.tunnelUrl || !info.attachToken) {
|
|
5969
|
+
throw new Error(
|
|
5970
|
+
"Workspace tunnels are unavailable for this task (GitHub Codespace-backed environments expose previews only \u2014 use workspace_preview_urls)."
|
|
5971
|
+
);
|
|
5972
|
+
}
|
|
5973
|
+
const remotePort = port ?? info.ssh?.remotePort ?? 2222;
|
|
5974
|
+
const handle = await startTunnel({
|
|
5975
|
+
remotePort,
|
|
5976
|
+
preferredLocalPort,
|
|
5977
|
+
// Re-mint per connection so the tunnel outlives the attach-token TTL.
|
|
5978
|
+
// The key (if any) is already installed by the call above; the refetch
|
|
5979
|
+
// only needs a fresh token, so it omits sshPublicKey.
|
|
5980
|
+
resolveTarget: async () => {
|
|
5981
|
+
const fresh = await conn2.getWorkspaceAttachInfo(taskId);
|
|
5982
|
+
if (!fresh.tunnelUrl || !fresh.attachToken)
|
|
5983
|
+
throw new Error("Workspace tunnel target is no longer available");
|
|
5984
|
+
return { tunnelUrl: fresh.tunnelUrl, attachToken: fresh.attachToken };
|
|
5985
|
+
}
|
|
5986
|
+
});
|
|
5987
|
+
const tunnelId = createTunnelId(taskId, remotePort);
|
|
5988
|
+
activeTunnels.set(tunnelId, handle);
|
|
5989
|
+
return {
|
|
5990
|
+
content: [
|
|
5991
|
+
{
|
|
5992
|
+
type: "text",
|
|
5993
|
+
text: JSON.stringify(
|
|
5994
|
+
{
|
|
5995
|
+
tunnelId,
|
|
5996
|
+
taskId,
|
|
5997
|
+
remotePort,
|
|
5998
|
+
localHost: handle.host,
|
|
5999
|
+
localPort: handle.port,
|
|
6000
|
+
localUrl: localUrlFor(remotePort, handle.host, handle.port),
|
|
6001
|
+
// `-o ServerAliveInterval=30` drives client-side keepalives so an
|
|
6002
|
+
// idle session keeps the tunnel warm (the tunnel layer pings too,
|
|
6003
|
+
// but the client must keep its own TCP path alive across every hop).
|
|
6004
|
+
sshCommand: remotePort === 2222 ? `ssh -o ServerAliveInterval=30 -o ServerAliveCountMax=3 -p ${handle.port} ${info.ssh?.username ?? "conveyor"}@${handle.host}` : void 0,
|
|
6005
|
+
sftpCommand: remotePort === 2222 ? `sftp -o ServerAliveInterval=30 -o ServerAliveCountMax=3 -P ${handle.port} ${info.ssh?.username ?? "conveyor"}@${handle.host}` : void 0,
|
|
6006
|
+
previewPorts: info.previewPorts,
|
|
6007
|
+
previewUrls: info.previewUrls,
|
|
6008
|
+
expiresAt: info.expiresAt
|
|
6009
|
+
},
|
|
6010
|
+
null,
|
|
6011
|
+
2
|
|
6012
|
+
)
|
|
6013
|
+
}
|
|
6014
|
+
]
|
|
6015
|
+
};
|
|
6016
|
+
}
|
|
6017
|
+
);
|
|
6018
|
+
}
|
|
6019
|
+
function registerStopTunnelTool(server2) {
|
|
6020
|
+
server2.tool(
|
|
6021
|
+
"workspace_stop_tunnel",
|
|
6022
|
+
"Stop a local workspace tunnel previously opened by workspace_start_tunnel.",
|
|
6023
|
+
{
|
|
6024
|
+
tunnelId: z13.string().describe("Tunnel id returned by workspace_start_tunnel")
|
|
6025
|
+
},
|
|
6026
|
+
async ({ tunnelId }) => {
|
|
6027
|
+
const tunnel = activeTunnels.get(tunnelId);
|
|
6028
|
+
if (!tunnel) throw new Error("Workspace tunnel not found");
|
|
6029
|
+
await tunnel.close();
|
|
6030
|
+
activeTunnels.delete(tunnelId);
|
|
6031
|
+
return { content: [{ type: "text", text: "Workspace tunnel stopped" }] };
|
|
6032
|
+
}
|
|
6033
|
+
);
|
|
6034
|
+
}
|
|
6035
|
+
function registerWorkspaceTools(server2, conn2, deps = {}) {
|
|
6036
|
+
registerAttachInfoTool(server2, conn2);
|
|
6037
|
+
registerPreviewUrlsTool(server2, conn2);
|
|
6038
|
+
registerStartTunnelTool(server2, conn2, deps.startTunnel ?? startWorkspaceSshTunnel);
|
|
6039
|
+
registerStopTunnelTool(server2);
|
|
6040
|
+
}
|
|
6041
|
+
|
|
6042
|
+
// src/tools/logs.ts
|
|
6043
|
+
function registerLogTools(server2, conn2) {
|
|
6044
|
+
registerContractTool(server2, queryGcpLogsContract, async (params) => {
|
|
6045
|
+
const text = await runQueryGcpLogs(conn2, params);
|
|
6046
|
+
return { content: [{ type: "text", text }] };
|
|
6047
|
+
});
|
|
6048
|
+
registerContractTool(server2, queryGrafanaLogsContract, async (params) => {
|
|
6049
|
+
const text = await runQueryGrafanaLogs(conn2, params);
|
|
6050
|
+
return { content: [{ type: "text", text }] };
|
|
6051
|
+
});
|
|
6052
|
+
}
|
|
6053
|
+
|
|
6054
|
+
// src/tools/integrations.ts
|
|
6055
|
+
function registerIntegrationTools(server2, conn2) {
|
|
6056
|
+
registerContractTool(server2, listProjectIntegrationsContract, async (params) => {
|
|
6057
|
+
const result = await conn2.listProjectIntegrations(params.projectId);
|
|
6058
|
+
return { content: [{ type: "text", text: JSON.stringify(result, null, 2) }] };
|
|
6059
|
+
});
|
|
6060
|
+
registerContractTool(server2, listProjectChannelsContract, async (params) => {
|
|
6061
|
+
const channels = await conn2.listProjectChannels(params.projectId);
|
|
5799
6062
|
if (channels.length === 0) {
|
|
5800
6063
|
return {
|
|
5801
6064
|
content: [
|
|
@@ -6027,6 +6290,14 @@ function registerDecisionTools(server2, conn2) {
|
|
|
6027
6290
|
registerDecisionWrites(server2, conn2);
|
|
6028
6291
|
}
|
|
6029
6292
|
|
|
6293
|
+
// src/tools/evaluate.ts
|
|
6294
|
+
function registerEvaluateTools(server2, conn2) {
|
|
6295
|
+
registerContractTool(server2, evaluateQuestionsContract, async (params) => {
|
|
6296
|
+
const res = await conn2.evaluateQuestions(params);
|
|
6297
|
+
return { content: [{ type: "text", text: formatEvaluateQuestionsResult(res) }] };
|
|
6298
|
+
});
|
|
6299
|
+
}
|
|
6300
|
+
|
|
6030
6301
|
// src/tools/index.ts
|
|
6031
6302
|
function registerAllTools(server2, conn2) {
|
|
6032
6303
|
registerProjectTools(server2, conn2);
|
|
@@ -6046,6 +6317,7 @@ function registerAllTools(server2, conn2) {
|
|
|
6046
6317
|
registerDriveTools(server2, conn2);
|
|
6047
6318
|
registerMeetingTools(server2, conn2);
|
|
6048
6319
|
registerDecisionTools(server2, conn2);
|
|
6320
|
+
registerEvaluateTools(server2, conn2);
|
|
6049
6321
|
}
|
|
6050
6322
|
|
|
6051
6323
|
// src/cli.ts
|