@rallycry/conveyor-mcp 5.0.8 → 5.0.10
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-L3AI3EU2.js → chunk-UPLS63HM.js} +15 -3
- package/dist/cli.js +974 -779
- package/dist/{connection-K8I1nT9o.d.ts → connection-BJpUPf8O.d.ts} +18 -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,14 +72,24 @@ 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-3D2OVVIS.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";
|
|
80
81
|
var FABLE_MODEL = "claude-fable-5-1";
|
|
81
82
|
var PREVIOUS_SONNET_MODEL = "claude-sonnet-4-6";
|
|
82
83
|
var PREVIOUS_OPUS_MODEL = "claude-opus-5";
|
|
84
|
+
function formatModelId(provider, model) {
|
|
85
|
+
return `${provider}/${model}`;
|
|
86
|
+
}
|
|
87
|
+
var JEV_MODEL = "jev-1.13.0";
|
|
88
|
+
var JEV_MODEL_ID = formatModelId("typesafe", JEV_MODEL);
|
|
89
|
+
var JEV_ZEN_MODEL = "jev-1.13";
|
|
90
|
+
var JEV_ZEN_MODEL_ID = formatModelId("zen", JEV_ZEN_MODEL);
|
|
91
|
+
var JEV_INPUT_PRICE_PER_TOKEN = 0.042 / 1e6;
|
|
92
|
+
var JEV_STATE_MAX_CHARS = 1e5;
|
|
83
93
|
var PTY_STREAM_PORT_BASE = 7420;
|
|
84
94
|
var PTY_STREAM_PORT_ATTEMPTS = 8;
|
|
85
95
|
var PREVIEW_PORT_DENY_LIST = [
|
|
@@ -146,6 +156,12 @@ var projectCheckpointSettingsSchema = z2.object({
|
|
|
146
156
|
var CARD_DESCRIPTION_MAX = 255;
|
|
147
157
|
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
158
|
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`;
|
|
159
|
+
var MAX_MANUAL_TESTS_PER_TASK = 5;
|
|
160
|
+
var MAX_MANUAL_TEST_TITLE_LENGTH = 200;
|
|
161
|
+
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);
|
|
162
|
+
function describeManualTestRejection(item) {
|
|
163
|
+
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.`;
|
|
164
|
+
}
|
|
149
165
|
var DEFAULT_CI_WAIT_TIMEOUT_MINUTES = 45;
|
|
150
166
|
var MAX_CI_WAIT_TIMEOUT_MINUTES = 180;
|
|
151
167
|
var SEVERITY_ENUM = [
|
|
@@ -306,14 +322,14 @@ var f = {
|
|
|
306
322
|
return { kind: "nullable", inner };
|
|
307
323
|
}
|
|
308
324
|
};
|
|
309
|
-
function compileString(
|
|
310
|
-
let schema =
|
|
325
|
+
function compileString(z14, spec) {
|
|
326
|
+
let schema = z14.string();
|
|
311
327
|
if (spec.min !== void 0) schema = schema.min(spec.min);
|
|
312
328
|
if (spec.max !== void 0) schema = schema.max(spec.max);
|
|
313
329
|
return schema;
|
|
314
330
|
}
|
|
315
|
-
function compileNumber(
|
|
316
|
-
let schema =
|
|
331
|
+
function compileNumber(z14, spec) {
|
|
332
|
+
let schema = z14.number();
|
|
317
333
|
if (spec.int) schema = schema.int();
|
|
318
334
|
if (spec.positive) schema = schema.positive();
|
|
319
335
|
if (spec.nonnegative) schema = schema.nonnegative();
|
|
@@ -321,49 +337,50 @@ function compileNumber(z13, spec) {
|
|
|
321
337
|
if (spec.max !== void 0) schema = schema.max(spec.max);
|
|
322
338
|
return schema;
|
|
323
339
|
}
|
|
324
|
-
function compileArray(
|
|
325
|
-
let schema =
|
|
340
|
+
function compileArray(z14, spec) {
|
|
341
|
+
let schema = z14.array(compileField(z14, spec.item));
|
|
326
342
|
if (spec.min !== void 0) schema = schema.min(spec.min);
|
|
343
|
+
if (spec.max !== void 0) schema = schema.max(spec.max);
|
|
327
344
|
return schema;
|
|
328
345
|
}
|
|
329
|
-
function compileBase(
|
|
346
|
+
function compileBase(z14, spec) {
|
|
330
347
|
switch (spec.kind) {
|
|
331
348
|
case "string":
|
|
332
|
-
return compileString(
|
|
349
|
+
return compileString(z14, spec);
|
|
333
350
|
case "number":
|
|
334
|
-
return compileNumber(
|
|
351
|
+
return compileNumber(z14, spec);
|
|
335
352
|
case "boolean":
|
|
336
|
-
return
|
|
353
|
+
return z14.boolean();
|
|
337
354
|
case "enum":
|
|
338
|
-
return
|
|
355
|
+
return z14.enum([...spec.values]);
|
|
339
356
|
case "array":
|
|
340
|
-
return compileArray(
|
|
357
|
+
return compileArray(z14, spec);
|
|
341
358
|
case "object":
|
|
342
|
-
return
|
|
359
|
+
return z14.object(compileShape(z14, spec.fields));
|
|
343
360
|
}
|
|
344
361
|
}
|
|
345
362
|
function descriptionOf(spec) {
|
|
346
363
|
if (spec.kind === "optional" || spec.kind === "nullable") return descriptionOf(spec.inner);
|
|
347
364
|
return spec.desc;
|
|
348
365
|
}
|
|
349
|
-
function compileUndescribed(
|
|
366
|
+
function compileUndescribed(z14, spec) {
|
|
350
367
|
if (spec.kind === "optional") {
|
|
351
|
-
return compileUndescribed(
|
|
368
|
+
return compileUndescribed(z14, spec.inner).optional();
|
|
352
369
|
}
|
|
353
370
|
if (spec.kind === "nullable") {
|
|
354
|
-
return compileUndescribed(
|
|
371
|
+
return compileUndescribed(z14, spec.inner).nullable();
|
|
355
372
|
}
|
|
356
|
-
return compileBase(
|
|
373
|
+
return compileBase(z14, spec);
|
|
357
374
|
}
|
|
358
|
-
function compileField(
|
|
359
|
-
const schema = compileUndescribed(
|
|
375
|
+
function compileField(z14, spec) {
|
|
376
|
+
const schema = compileUndescribed(z14, spec);
|
|
360
377
|
const desc = descriptionOf(spec);
|
|
361
378
|
return desc === void 0 ? schema : schema.describe(desc);
|
|
362
379
|
}
|
|
363
|
-
function compileShape(
|
|
380
|
+
function compileShape(z14, fields) {
|
|
364
381
|
const shape = {};
|
|
365
382
|
for (const [key, spec] of Object.entries(fields)) {
|
|
366
|
-
shape[key] = compileField(
|
|
383
|
+
shape[key] = compileField(z14, spec);
|
|
367
384
|
}
|
|
368
385
|
return shape;
|
|
369
386
|
}
|
|
@@ -453,16 +470,24 @@ var readTaskChatContract = defineToolContract({
|
|
|
453
470
|
}
|
|
454
471
|
}
|
|
455
472
|
});
|
|
473
|
+
var listTagsDetail = f.optional(
|
|
474
|
+
f.enum(["summary", "full"], {
|
|
475
|
+
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.`
|
|
476
|
+
})
|
|
477
|
+
);
|
|
456
478
|
var listTagsContract = defineToolContract({
|
|
457
479
|
name: "list_tags",
|
|
458
480
|
agent: {
|
|
459
|
-
description: "List the project glossary
|
|
460
|
-
fields: {
|
|
481
|
+
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.",
|
|
482
|
+
fields: {
|
|
483
|
+
detail: listTagsDetail
|
|
484
|
+
}
|
|
461
485
|
},
|
|
462
486
|
mcp: {
|
|
463
|
-
description:
|
|
487
|
+
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
488
|
fields: {
|
|
465
|
-
projectId: mcpProjectId
|
|
489
|
+
projectId: mcpProjectId,
|
|
490
|
+
detail: listTagsDetail
|
|
466
491
|
}
|
|
467
492
|
}
|
|
468
493
|
});
|
|
@@ -598,7 +623,7 @@ var updateTaskContract = defineToolContract({
|
|
|
598
623
|
}
|
|
599
624
|
},
|
|
600
625
|
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.",
|
|
626
|
+
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
627
|
fields: {
|
|
603
628
|
projectId: mcpProjectId,
|
|
604
629
|
taskId: f.string({ desc: "The task ID" }),
|
|
@@ -629,6 +654,11 @@ var updateTaskContract = defineToolContract({
|
|
|
629
654
|
)
|
|
630
655
|
),
|
|
631
656
|
githubBranch: f.optional(f.nullable(f.string({ desc: GITHUB_BRANCH_DESC }))),
|
|
657
|
+
onHold: f.optional(
|
|
658
|
+
f.boolean({
|
|
659
|
+
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."
|
|
660
|
+
})
|
|
661
|
+
),
|
|
632
662
|
addTags: f.optional(
|
|
633
663
|
f.array(f.string(), {
|
|
634
664
|
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 +702,20 @@ var testStatuses = f.optional(
|
|
|
672
702
|
})
|
|
673
703
|
);
|
|
674
704
|
var setManualTestItems = f.array(
|
|
675
|
-
f.object({
|
|
676
|
-
|
|
705
|
+
f.object({
|
|
706
|
+
title: f.string({
|
|
707
|
+
min: 1,
|
|
708
|
+
max: MAX_MANUAL_TEST_TITLE_LENGTH,
|
|
709
|
+
desc: "One sentence a non-engineer can follow: where to go, what to do, what they should see. No commands, no code."
|
|
710
|
+
})
|
|
711
|
+
}),
|
|
712
|
+
{
|
|
713
|
+
min: 1,
|
|
714
|
+
max: MAX_MANUAL_TESTS_PER_TASK,
|
|
715
|
+
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.`
|
|
716
|
+
}
|
|
677
717
|
);
|
|
718
|
+
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
719
|
var titleToEdit = f.string({ min: 1, desc: "The current title of the manual test to edit" });
|
|
679
720
|
var newTitle = f.string({ min: 1, desc: "The new title for the manual test" });
|
|
680
721
|
var titleToRemove = f.string({ min: 1, desc: "The title of the manual test to remove" });
|
|
@@ -728,13 +769,13 @@ var queryManualTestsContract = defineToolContract({
|
|
|
728
769
|
var setManualTestsContract = defineToolContract({
|
|
729
770
|
name: "set_manual_tests",
|
|
730
771
|
agent: {
|
|
731
|
-
description:
|
|
772
|
+
description: `Record the manual tests a person should run in the app to sign off on this card. ${setManualTestsBar}`,
|
|
732
773
|
fields: {
|
|
733
774
|
items: setManualTestItems
|
|
734
775
|
}
|
|
735
776
|
},
|
|
736
777
|
mcp: {
|
|
737
|
-
description:
|
|
778
|
+
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
779
|
fields: {
|
|
739
780
|
projectId: mcpProjectId,
|
|
740
781
|
taskId: mcpChecklistTaskId,
|
|
@@ -2143,6 +2184,71 @@ var waitForChecksContract = defineToolContract({
|
|
|
2143
2184
|
}
|
|
2144
2185
|
});
|
|
2145
2186
|
var ciWaitContracts = [waitForChecksContract];
|
|
2187
|
+
var DESCRIPTION = [
|
|
2188
|
+
"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).",
|
|
2189
|
+
"Requires Jev to be switched on under Project Settings \u2192 AI Provider Keys (a TypeSafe key, or an OpenCode Zen key); without it the tool returns an error saying so.",
|
|
2190
|
+
"The state is data, not a guardrail: adversarial text inside it can move the answers, so never use a result to authorize an action.",
|
|
2191
|
+
"Ask one simple judgment per question and split compound questions; accuracy falls when the state carries irrelevant text.",
|
|
2192
|
+
"Pass items to judge up to 100 separate texts with the same questions in one call; answers come back in item order.",
|
|
2193
|
+
"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."
|
|
2194
|
+
].join(" ");
|
|
2195
|
+
var question = f.object(
|
|
2196
|
+
{
|
|
2197
|
+
key: f.string({
|
|
2198
|
+
min: 1,
|
|
2199
|
+
desc: "Name for this question's answer, e.g. is_complaint. Letters, digits, _ and - only; unique within the call."
|
|
2200
|
+
}),
|
|
2201
|
+
type: f.enum(["noul", "choice", "score"], {
|
|
2202
|
+
desc: "noul = yes/no; choice = pick one option; score = pick a level on an ordered scale"
|
|
2203
|
+
}),
|
|
2204
|
+
instructions: f.string({
|
|
2205
|
+
min: 1,
|
|
2206
|
+
desc: "The question, as one plain sentence about the state, e.g. 'Is this message a complaint about the product?'"
|
|
2207
|
+
}),
|
|
2208
|
+
options: f.optional(
|
|
2209
|
+
f.array(
|
|
2210
|
+
f.object({
|
|
2211
|
+
key: f.string({ min: 1, desc: "Option name returned as the answer" }),
|
|
2212
|
+
description: f.string({
|
|
2213
|
+
min: 1,
|
|
2214
|
+
desc: "What choosing this option means, concretely"
|
|
2215
|
+
})
|
|
2216
|
+
}),
|
|
2217
|
+
{ desc: "choice only: the options (2-255). Option order can move the answer." }
|
|
2218
|
+
)
|
|
2219
|
+
),
|
|
2220
|
+
levels: f.optional(
|
|
2221
|
+
f.array(f.string({ min: 1 }), {
|
|
2222
|
+
desc: "score only: 2-10 level descriptions, lowest first"
|
|
2223
|
+
})
|
|
2224
|
+
)
|
|
2225
|
+
},
|
|
2226
|
+
{ desc: "One typed question" }
|
|
2227
|
+
);
|
|
2228
|
+
var stateDesc = "The thing being judged, as plain text or JSON. Required unless items is given; with items it is ignored.";
|
|
2229
|
+
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.";
|
|
2230
|
+
var questionsDesc = "1-32 questions, all answered independently against the same state.";
|
|
2231
|
+
var evaluateQuestionsContract = defineToolContract({
|
|
2232
|
+
name: "evaluate_questions",
|
|
2233
|
+
agent: {
|
|
2234
|
+
description: DESCRIPTION,
|
|
2235
|
+
fields: {
|
|
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
|
+
mcp: {
|
|
2242
|
+
description: `${DESCRIPTION} Pass projectId to target a specific project; otherwise the configured default project is used.`,
|
|
2243
|
+
fields: {
|
|
2244
|
+
projectId: mcpProjectId,
|
|
2245
|
+
state: f.optional(f.string({ desc: stateDesc })),
|
|
2246
|
+
questions: f.array(question, { min: 1, desc: questionsDesc }),
|
|
2247
|
+
items: f.optional(f.array(f.string(), { desc: itemsDesc }))
|
|
2248
|
+
}
|
|
2249
|
+
}
|
|
2250
|
+
});
|
|
2251
|
+
var evaluateContracts = [evaluateQuestionsContract];
|
|
2146
2252
|
var TOOL_CONTRACTS = Object.fromEntries(
|
|
2147
2253
|
[
|
|
2148
2254
|
...tasksContracts,
|
|
@@ -2159,7 +2265,8 @@ var TOOL_CONTRACTS = Object.fromEntries(
|
|
|
2159
2265
|
...driveContracts,
|
|
2160
2266
|
...meetingsContracts,
|
|
2161
2267
|
...logsContracts,
|
|
2162
|
-
...ciWaitContracts
|
|
2268
|
+
...ciWaitContracts,
|
|
2269
|
+
...evaluateContracts
|
|
2163
2270
|
].map((contract) => [contract.name, contract])
|
|
2164
2271
|
);
|
|
2165
2272
|
|
|
@@ -2597,6 +2704,7 @@ function registerListTasks(server2, conn2) {
|
|
|
2597
2704
|
assigneeId: z6.string().optional().describe("Filter by assigned user ID"),
|
|
2598
2705
|
unassigned: z6.boolean().optional().describe("Only return tasks with no assignee (mutually exclusive with assigneeId)"),
|
|
2599
2706
|
subProjectId: BOARD_FILTER,
|
|
2707
|
+
onHold: z6.boolean().optional().describe("true = only cards on hold, false = exclude them, omit = both"),
|
|
2600
2708
|
limit: z6.number().optional().describe("Max tasks to return (default 50)")
|
|
2601
2709
|
},
|
|
2602
2710
|
async (params) => {
|
|
@@ -2803,7 +2911,7 @@ function registerSearchTasks(server2, conn2) {
|
|
|
2803
2911
|
}
|
|
2804
2912
|
function registerListTags(server2, conn2) {
|
|
2805
2913
|
registerContractTool(server2, listTagsContract, async (params) => {
|
|
2806
|
-
const tags = await conn2.listTags(params.projectId);
|
|
2914
|
+
const tags = await conn2.listTags(params.projectId, params.detail);
|
|
2807
2915
|
return { content: [{ type: "text", text: JSON.stringify(tags, null, 2) }] };
|
|
2808
2916
|
});
|
|
2809
2917
|
}
|
|
@@ -3314,353 +3422,9 @@ function registerSuggestionTools(server2, conn2) {
|
|
|
3314
3422
|
});
|
|
3315
3423
|
}
|
|
3316
3424
|
|
|
3317
|
-
//
|
|
3318
|
-
function renderManualTestGroups(groups) {
|
|
3319
|
-
const lines = [];
|
|
3320
|
-
for (const g of groups) {
|
|
3321
|
-
lines.push(`## ${g.title} [${g.status}] (${g.slug})`);
|
|
3322
|
-
for (const t of g.tests) {
|
|
3323
|
-
const mark = t.status === "approved" ? "\u2713" : t.status === "rejected" ? "\u2717" : "\u25CB";
|
|
3324
|
-
lines.push(` ${mark} ${t.title} \u2014 ${t.status}`);
|
|
3325
|
-
if (t.status === "rejected") {
|
|
3326
|
-
for (const f2 of t.failures ?? []) {
|
|
3327
|
-
lines.push(` \u26A0 ${f2.userName ?? "Someone"}: ${f2.reason ?? "(no message)"}`);
|
|
3328
|
-
}
|
|
3329
|
-
}
|
|
3330
|
-
}
|
|
3331
|
-
lines.push("");
|
|
3332
|
-
}
|
|
3333
|
-
return lines.join("\n").trimEnd();
|
|
3334
|
-
}
|
|
3335
|
-
function registerListManualTests(server2, conn2) {
|
|
3336
|
-
registerContractTool(server2, listManualTestsContract, async (params) => {
|
|
3337
|
-
const items = await conn2.listManualTests(params.taskId, params.projectId);
|
|
3338
|
-
if (items.length === 0) {
|
|
3339
|
-
return { content: [{ type: "text", text: "No manual tests recorded for this task." }] };
|
|
3340
|
-
}
|
|
3341
|
-
const lines = items.flatMap((item, i) => {
|
|
3342
|
-
const checked = item.checked ? "[x]" : "[ ]";
|
|
3343
|
-
const row = [`${i + 1}. ${checked} ${item.title}`];
|
|
3344
|
-
for (const f2 of item.failures ?? []) {
|
|
3345
|
-
const who = f2.userName ?? "Someone";
|
|
3346
|
-
const reason = f2.reason ?? "(no message)";
|
|
3347
|
-
row.push(` \u26A0 Failed (${who}): ${reason}`);
|
|
3348
|
-
}
|
|
3349
|
-
return row;
|
|
3350
|
-
});
|
|
3351
|
-
return { content: [{ type: "text", text: lines.join("\n") }] };
|
|
3352
|
-
});
|
|
3353
|
-
}
|
|
3354
|
-
function registerSetManualTests(server2, conn2) {
|
|
3355
|
-
registerContractTool(server2, setManualTestsContract, async (params) => {
|
|
3356
|
-
const result = await conn2.setManualTests(params.taskId, params.items, params.projectId);
|
|
3357
|
-
const parts = [`Created ${result.created} manual test item(s).`];
|
|
3358
|
-
if (result.skipped > 0) parts.push(`Skipped ${result.skipped} duplicate(s).`);
|
|
3359
|
-
return { content: [{ type: "text", text: parts.join(" ") }] };
|
|
3360
|
-
});
|
|
3361
|
-
}
|
|
3362
|
-
function registerEditManualTest(server2, conn2) {
|
|
3363
|
-
registerContractTool(server2, editManualTestContract, async (params) => {
|
|
3364
|
-
await conn2.editManualTest(params.taskId, params.title, params.newTitle, params.projectId);
|
|
3365
|
-
return { content: [{ type: "text", text: `Updated manual test to "${params.newTitle}".` }] };
|
|
3366
|
-
});
|
|
3367
|
-
}
|
|
3368
|
-
function registerRemoveManualTest(server2, conn2) {
|
|
3369
|
-
registerContractTool(server2, removeManualTestContract, async (params) => {
|
|
3370
|
-
await conn2.removeManualTest(params.taskId, params.title, params.projectId);
|
|
3371
|
-
return { content: [{ type: "text", text: `Removed manual test "${params.title}".` }] };
|
|
3372
|
-
});
|
|
3373
|
-
}
|
|
3374
|
-
function registerApproveManualTest(server2, conn2) {
|
|
3375
|
-
registerContractTool(server2, approveManualTestContract, async (params) => {
|
|
3376
|
-
await conn2.approveManualTest(params.taskId, params.title, params.projectId);
|
|
3377
|
-
return { content: [{ type: "text", text: `Approved manual test "${params.title}".` }] };
|
|
3378
|
-
});
|
|
3379
|
-
}
|
|
3380
|
-
function registerRejectManualTest(server2, conn2) {
|
|
3381
|
-
registerContractTool(server2, rejectManualTestContract, async (params) => {
|
|
3382
|
-
await conn2.rejectManualTest(params.taskId, params.title, params.reason, params.projectId);
|
|
3383
|
-
return {
|
|
3384
|
-
content: [
|
|
3385
|
-
{
|
|
3386
|
-
type: "text",
|
|
3387
|
-
text: `Flagged an issue with manual test "${params.title}": ${params.reason}`
|
|
3388
|
-
}
|
|
3389
|
-
]
|
|
3390
|
-
};
|
|
3391
|
-
});
|
|
3392
|
-
}
|
|
3393
|
-
function registerQueryManualTests(server2, conn2) {
|
|
3394
|
-
registerContractTool(server2, queryManualTestsContract, async (params) => {
|
|
3395
|
-
const groups = await conn2.queryManualTests({
|
|
3396
|
-
projectId: params.projectId,
|
|
3397
|
-
cardStatuses: params.cardStatuses,
|
|
3398
|
-
testStatuses: params.testStatuses
|
|
3399
|
-
});
|
|
3400
|
-
if (groups.length === 0) {
|
|
3401
|
-
return { content: [{ type: "text", text: "No manual tests match those filters." }] };
|
|
3402
|
-
}
|
|
3403
|
-
return { content: [{ type: "text", text: renderManualTestGroups(groups) }] };
|
|
3404
|
-
});
|
|
3405
|
-
}
|
|
3406
|
-
function registerChecklistTools(server2, conn2) {
|
|
3407
|
-
registerListManualTests(server2, conn2);
|
|
3408
|
-
registerQueryManualTests(server2, conn2);
|
|
3409
|
-
registerSetManualTests(server2, conn2);
|
|
3410
|
-
registerEditManualTest(server2, conn2);
|
|
3411
|
-
registerRemoveManualTest(server2, conn2);
|
|
3412
|
-
registerApproveManualTest(server2, conn2);
|
|
3413
|
-
registerRejectManualTest(server2, conn2);
|
|
3414
|
-
}
|
|
3415
|
-
|
|
3416
|
-
// src/tools/workspace.ts
|
|
3425
|
+
// ../shared/dist/index.js
|
|
3417
3426
|
import { z as z9 } from "zod";
|
|
3418
|
-
|
|
3419
|
-
// src/workspace-ssh-tunnel.ts
|
|
3420
|
-
import net from "net";
|
|
3421
|
-
import { once } from "events";
|
|
3422
|
-
import { WebSocket } from "ws";
|
|
3423
|
-
var WORKSPACE_TUNNEL_HEARTBEAT_MS = 3e4;
|
|
3424
|
-
function withPort(url, port) {
|
|
3425
|
-
const parsed = new URL(url);
|
|
3426
|
-
parsed.searchParams.set("port", String(port));
|
|
3427
|
-
return parsed.toString();
|
|
3428
|
-
}
|
|
3429
|
-
function toBuffer(data) {
|
|
3430
|
-
if (Buffer.isBuffer(data)) return data;
|
|
3431
|
-
if (data instanceof ArrayBuffer) return Buffer.from(data);
|
|
3432
|
-
return Buffer.concat(data);
|
|
3433
|
-
}
|
|
3434
|
-
function startHeartbeat(ws) {
|
|
3435
|
-
const timer = setInterval(() => {
|
|
3436
|
-
if (ws.readyState === WebSocket.OPEN) ws.ping();
|
|
3437
|
-
}, WORKSPACE_TUNNEL_HEARTBEAT_MS);
|
|
3438
|
-
timer.unref?.();
|
|
3439
|
-
return () => clearInterval(timer);
|
|
3440
|
-
}
|
|
3441
|
-
function bridgeClient(client, options) {
|
|
3442
|
-
let ws = null;
|
|
3443
|
-
let ready = false;
|
|
3444
|
-
const pending = [];
|
|
3445
|
-
client.on("data", (data) => {
|
|
3446
|
-
const chunk = Buffer.isBuffer(data) ? data : Buffer.from(data);
|
|
3447
|
-
if (ready && ws && ws.readyState === WebSocket.OPEN) ws.send(chunk);
|
|
3448
|
-
else pending.push(chunk);
|
|
3449
|
-
});
|
|
3450
|
-
client.on("close", () => ws?.close());
|
|
3451
|
-
client.on("error", () => ws?.close());
|
|
3452
|
-
options.resolveTarget().then(({ tunnelUrl, attachToken }) => {
|
|
3453
|
-
const socket = new WebSocket(withPort(tunnelUrl, options.remotePort), {
|
|
3454
|
-
headers: { "x-workspace-attach-token": attachToken }
|
|
3455
|
-
});
|
|
3456
|
-
ws = socket;
|
|
3457
|
-
const stopHeartbeat = startHeartbeat(socket);
|
|
3458
|
-
socket.on("message", (data) => {
|
|
3459
|
-
const chunk = toBuffer(data);
|
|
3460
|
-
if (!ready) {
|
|
3461
|
-
const text = chunk.toString("utf8");
|
|
3462
|
-
if (text === JSON.stringify({ ok: true })) {
|
|
3463
|
-
ready = true;
|
|
3464
|
-
for (const buffered of pending.splice(0)) socket.send(buffered);
|
|
3465
|
-
return;
|
|
3466
|
-
}
|
|
3467
|
-
client.destroy(new Error(`Workspace tunnel rejected connection: ${text}`));
|
|
3468
|
-
socket.close();
|
|
3469
|
-
return;
|
|
3470
|
-
}
|
|
3471
|
-
client.write(chunk);
|
|
3472
|
-
});
|
|
3473
|
-
socket.on("close", () => {
|
|
3474
|
-
stopHeartbeat();
|
|
3475
|
-
client.destroy();
|
|
3476
|
-
});
|
|
3477
|
-
socket.on("error", (err) => {
|
|
3478
|
-
stopHeartbeat();
|
|
3479
|
-
client.destroy(err);
|
|
3480
|
-
});
|
|
3481
|
-
}).catch((err) => {
|
|
3482
|
-
client.destroy(err instanceof Error ? err : new Error(String(err)));
|
|
3483
|
-
});
|
|
3484
|
-
}
|
|
3485
|
-
async function startWorkspaceSshTunnel(options) {
|
|
3486
|
-
const host = options.host ?? "127.0.0.1";
|
|
3487
|
-
const server2 = net.createServer((client) => bridgeClient(client, options));
|
|
3488
|
-
const ac = new AbortController();
|
|
3489
|
-
server2.listen(options.preferredLocalPort ?? 0, host);
|
|
3490
|
-
try {
|
|
3491
|
-
await Promise.race([
|
|
3492
|
-
once(server2, "listening", { signal: ac.signal }),
|
|
3493
|
-
once(server2, "error", { signal: ac.signal }).then(([err]) => {
|
|
3494
|
-
throw err instanceof Error ? err : new Error(String(err));
|
|
3495
|
-
})
|
|
3496
|
-
]);
|
|
3497
|
-
} catch (err) {
|
|
3498
|
-
server2.close();
|
|
3499
|
-
throw new Error(
|
|
3500
|
-
`Failed to bind workspace SSH tunnel: ${err instanceof Error ? err.message : String(err)}`,
|
|
3501
|
-
{ cause: err }
|
|
3502
|
-
);
|
|
3503
|
-
} finally {
|
|
3504
|
-
ac.abort();
|
|
3505
|
-
}
|
|
3506
|
-
const address = server2.address();
|
|
3507
|
-
if (!address || typeof address === "string") {
|
|
3508
|
-
server2.close();
|
|
3509
|
-
throw new Error("Failed to bind workspace SSH tunnel");
|
|
3510
|
-
}
|
|
3511
|
-
return {
|
|
3512
|
-
host,
|
|
3513
|
-
port: address.port,
|
|
3514
|
-
close: () => new Promise((resolve, reject) => {
|
|
3515
|
-
server2.close((err) => {
|
|
3516
|
-
if (err) reject(err);
|
|
3517
|
-
else resolve();
|
|
3518
|
-
});
|
|
3519
|
-
})
|
|
3520
|
-
};
|
|
3521
|
-
}
|
|
3522
|
-
|
|
3523
|
-
// src/tools/workspace.ts
|
|
3524
|
-
var activeTunnels = /* @__PURE__ */ new Map();
|
|
3525
|
-
function createTunnelId(taskId, port) {
|
|
3526
|
-
return `${taskId}:${port}:${Date.now().toString(36)}:${Math.random().toString(36).slice(2, 8)}`;
|
|
3527
|
-
}
|
|
3528
|
-
function localUrlFor(port, host, localPort) {
|
|
3529
|
-
if (port === 2222) return void 0;
|
|
3530
|
-
return `http://${host}:${localPort}`;
|
|
3531
|
-
}
|
|
3532
|
-
function registerAttachInfoTool(server2, conn2) {
|
|
3533
|
-
server2.tool(
|
|
3534
|
-
"workspace_attach_info",
|
|
3535
|
-
"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.",
|
|
3536
|
-
{
|
|
3537
|
-
taskId: z9.string().describe("The task ID"),
|
|
3538
|
-
sshPublicKey: z9.string().optional().describe("Optional OpenSSH public key to install into the workspace")
|
|
3539
|
-
},
|
|
3540
|
-
async ({ taskId, sshPublicKey }) => {
|
|
3541
|
-
const info = await conn2.getWorkspaceAttachInfo(taskId, sshPublicKey);
|
|
3542
|
-
return { content: [{ type: "text", text: JSON.stringify(info, null, 2) }] };
|
|
3543
|
-
}
|
|
3544
|
-
);
|
|
3545
|
-
}
|
|
3546
|
-
function registerPreviewUrlsTool(server2, conn2) {
|
|
3547
|
-
server2.tool(
|
|
3548
|
-
"workspace_preview_urls",
|
|
3549
|
-
"Return the hosted preview URLs and preview ports for a running task Claudespace. This mirrors the web UI preview link metadata.",
|
|
3550
|
-
{
|
|
3551
|
-
taskId: z9.string().describe("The task ID")
|
|
3552
|
-
},
|
|
3553
|
-
async ({ taskId }) => {
|
|
3554
|
-
const info = await conn2.getWorkspaceAttachInfo(taskId);
|
|
3555
|
-
return {
|
|
3556
|
-
content: [
|
|
3557
|
-
{
|
|
3558
|
-
type: "text",
|
|
3559
|
-
text: JSON.stringify(
|
|
3560
|
-
{
|
|
3561
|
-
taskId: info.taskId,
|
|
3562
|
-
sessionId: info.sessionId,
|
|
3563
|
-
previewPorts: info.previewPorts,
|
|
3564
|
-
previewUrls: info.previewUrls
|
|
3565
|
-
},
|
|
3566
|
-
null,
|
|
3567
|
-
2
|
|
3568
|
-
)
|
|
3569
|
-
}
|
|
3570
|
-
]
|
|
3571
|
-
};
|
|
3572
|
-
}
|
|
3573
|
-
);
|
|
3574
|
-
}
|
|
3575
|
-
function registerStartTunnelTool(server2, conn2, startTunnel) {
|
|
3576
|
-
server2.tool(
|
|
3577
|
-
"workspace_start_tunnel",
|
|
3578
|
-
"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.",
|
|
3579
|
-
{
|
|
3580
|
-
taskId: z9.string().describe("The task ID"),
|
|
3581
|
-
port: z9.number().optional().describe("Remote Claudespace port. Defaults to SSH port 2222."),
|
|
3582
|
-
preferredLocalPort: z9.number().optional().describe("Preferred local loopback port. If omitted, the OS chooses one."),
|
|
3583
|
-
sshPublicKey: z9.string().optional().describe("Optional OpenSSH public key to install before opening the tunnel")
|
|
3584
|
-
},
|
|
3585
|
-
async ({ taskId, port, preferredLocalPort, sshPublicKey }) => {
|
|
3586
|
-
const info = await conn2.getWorkspaceAttachInfo(taskId, sshPublicKey);
|
|
3587
|
-
if (!info.tunnelUrl || !info.attachToken) {
|
|
3588
|
-
throw new Error(
|
|
3589
|
-
"Workspace tunnels are unavailable for this task (GitHub Codespace-backed environments expose previews only \u2014 use workspace_preview_urls)."
|
|
3590
|
-
);
|
|
3591
|
-
}
|
|
3592
|
-
const remotePort = port ?? info.ssh?.remotePort ?? 2222;
|
|
3593
|
-
const handle = await startTunnel({
|
|
3594
|
-
remotePort,
|
|
3595
|
-
preferredLocalPort,
|
|
3596
|
-
// Re-mint per connection so the tunnel outlives the attach-token TTL.
|
|
3597
|
-
// The key (if any) is already installed by the call above; the refetch
|
|
3598
|
-
// only needs a fresh token, so it omits sshPublicKey.
|
|
3599
|
-
resolveTarget: async () => {
|
|
3600
|
-
const fresh = await conn2.getWorkspaceAttachInfo(taskId);
|
|
3601
|
-
if (!fresh.tunnelUrl || !fresh.attachToken)
|
|
3602
|
-
throw new Error("Workspace tunnel target is no longer available");
|
|
3603
|
-
return { tunnelUrl: fresh.tunnelUrl, attachToken: fresh.attachToken };
|
|
3604
|
-
}
|
|
3605
|
-
});
|
|
3606
|
-
const tunnelId = createTunnelId(taskId, remotePort);
|
|
3607
|
-
activeTunnels.set(tunnelId, handle);
|
|
3608
|
-
return {
|
|
3609
|
-
content: [
|
|
3610
|
-
{
|
|
3611
|
-
type: "text",
|
|
3612
|
-
text: JSON.stringify(
|
|
3613
|
-
{
|
|
3614
|
-
tunnelId,
|
|
3615
|
-
taskId,
|
|
3616
|
-
remotePort,
|
|
3617
|
-
localHost: handle.host,
|
|
3618
|
-
localPort: handle.port,
|
|
3619
|
-
localUrl: localUrlFor(remotePort, handle.host, handle.port),
|
|
3620
|
-
// `-o ServerAliveInterval=30` drives client-side keepalives so an
|
|
3621
|
-
// idle session keeps the tunnel warm (the tunnel layer pings too,
|
|
3622
|
-
// but the client must keep its own TCP path alive across every hop).
|
|
3623
|
-
sshCommand: remotePort === 2222 ? `ssh -o ServerAliveInterval=30 -o ServerAliveCountMax=3 -p ${handle.port} ${info.ssh?.username ?? "conveyor"}@${handle.host}` : void 0,
|
|
3624
|
-
sftpCommand: remotePort === 2222 ? `sftp -o ServerAliveInterval=30 -o ServerAliveCountMax=3 -P ${handle.port} ${info.ssh?.username ?? "conveyor"}@${handle.host}` : void 0,
|
|
3625
|
-
previewPorts: info.previewPorts,
|
|
3626
|
-
previewUrls: info.previewUrls,
|
|
3627
|
-
expiresAt: info.expiresAt
|
|
3628
|
-
},
|
|
3629
|
-
null,
|
|
3630
|
-
2
|
|
3631
|
-
)
|
|
3632
|
-
}
|
|
3633
|
-
]
|
|
3634
|
-
};
|
|
3635
|
-
}
|
|
3636
|
-
);
|
|
3637
|
-
}
|
|
3638
|
-
function registerStopTunnelTool(server2) {
|
|
3639
|
-
server2.tool(
|
|
3640
|
-
"workspace_stop_tunnel",
|
|
3641
|
-
"Stop a local workspace tunnel previously opened by workspace_start_tunnel.",
|
|
3642
|
-
{
|
|
3643
|
-
tunnelId: z9.string().describe("Tunnel id returned by workspace_start_tunnel")
|
|
3644
|
-
},
|
|
3645
|
-
async ({ tunnelId }) => {
|
|
3646
|
-
const tunnel = activeTunnels.get(tunnelId);
|
|
3647
|
-
if (!tunnel) throw new Error("Workspace tunnel not found");
|
|
3648
|
-
await tunnel.close();
|
|
3649
|
-
activeTunnels.delete(tunnelId);
|
|
3650
|
-
return { content: [{ type: "text", text: "Workspace tunnel stopped" }] };
|
|
3651
|
-
}
|
|
3652
|
-
);
|
|
3653
|
-
}
|
|
3654
|
-
function registerWorkspaceTools(server2, conn2, deps = {}) {
|
|
3655
|
-
registerAttachInfoTool(server2, conn2);
|
|
3656
|
-
registerPreviewUrlsTool(server2, conn2);
|
|
3657
|
-
registerStartTunnelTool(server2, conn2, deps.startTunnel ?? startWorkspaceSshTunnel);
|
|
3658
|
-
registerStopTunnelTool(server2);
|
|
3659
|
-
}
|
|
3660
|
-
|
|
3661
|
-
// ../shared/dist/index.js
|
|
3662
|
-
import { z as z10 } from "zod";
|
|
3663
|
-
import { z as z22 } from "zod";
|
|
3427
|
+
import { z as z23 } from "zod";
|
|
3664
3428
|
import { z as z32 } from "zod";
|
|
3665
3429
|
import { z as z42 } from "zod";
|
|
3666
3430
|
import { z as z52 } from "zod";
|
|
@@ -3668,7 +3432,7 @@ import { z as z62 } from "zod";
|
|
|
3668
3432
|
import { z as z72 } from "zod";
|
|
3669
3433
|
import { z as z82 } from "zod";
|
|
3670
3434
|
import { z as z92 } from "zod";
|
|
3671
|
-
import { z as
|
|
3435
|
+
import { z as z10 } from "zod";
|
|
3672
3436
|
import { z as z11 } from "zod";
|
|
3673
3437
|
import { z as z12 } from "zod";
|
|
3674
3438
|
var ACHIEVEMENT_RARITIES = [
|
|
@@ -3694,7 +3458,7 @@ var ACHIEVEMENT_RARITIES = [
|
|
|
3694
3458
|
{ key: "pack", name: "Pack", color: "#9c27b0", iconPath: "/storypoints/pack.svg" }
|
|
3695
3459
|
];
|
|
3696
3460
|
var RISK_LEVELS = ["critical", "high", "medium", "low"];
|
|
3697
|
-
var riskLevelSchema =
|
|
3461
|
+
var riskLevelSchema = z9.enum(RISK_LEVELS);
|
|
3698
3462
|
var DEFAULT_RISK_LEVELS = [
|
|
3699
3463
|
{
|
|
3700
3464
|
level: "critical",
|
|
@@ -3777,155 +3541,157 @@ var ALLOWED_FILE_ACCEPT = [
|
|
|
3777
3541
|
var EMBED_THRESHOLD_IMAGES = 5 * 1024 * 1024;
|
|
3778
3542
|
var EMBED_THRESHOLD_TEXT = 2 * 1024 * 1024;
|
|
3779
3543
|
var IDLE_HEARTBEAT_MS = 90 * 1e3;
|
|
3780
|
-
var TurnEndToolCallSchema =
|
|
3781
|
-
tool:
|
|
3782
|
-
input:
|
|
3783
|
-
output:
|
|
3784
|
-
timestamp:
|
|
3544
|
+
var TurnEndToolCallSchema = z23.object({
|
|
3545
|
+
tool: z23.string(),
|
|
3546
|
+
input: z23.string().optional(),
|
|
3547
|
+
output: z23.string().optional(),
|
|
3548
|
+
timestamp: z23.string().optional()
|
|
3785
3549
|
}).passthrough();
|
|
3786
|
-
var AgentTurnUsageSchema =
|
|
3787
|
-
inputTokens:
|
|
3788
|
-
outputTokens:
|
|
3789
|
-
cacheReadInputTokens:
|
|
3790
|
-
cacheCreationInputTokens:
|
|
3791
|
-
totalTokens:
|
|
3792
|
-
});
|
|
3793
|
-
var KnownAgentEventSchema =
|
|
3550
|
+
var AgentTurnUsageSchema = z23.object({
|
|
3551
|
+
inputTokens: z23.number(),
|
|
3552
|
+
outputTokens: z23.number(),
|
|
3553
|
+
cacheReadInputTokens: z23.number(),
|
|
3554
|
+
cacheCreationInputTokens: z23.number(),
|
|
3555
|
+
totalTokens: z23.number()
|
|
3556
|
+
});
|
|
3557
|
+
var KnownAgentEventSchema = z23.discriminatedUnion("type", [
|
|
3794
3558
|
// ── Lifecycle / connection ────────────────────────────────────────────
|
|
3795
|
-
|
|
3796
|
-
type:
|
|
3797
|
-
sessionId:
|
|
3798
|
-
projectId:
|
|
3559
|
+
z23.object({
|
|
3560
|
+
type: z23.literal("connected"),
|
|
3561
|
+
sessionId: z23.string(),
|
|
3562
|
+
projectId: z23.string().optional()
|
|
3799
3563
|
}).passthrough(),
|
|
3800
3564
|
// Open-ended context snapshot spread from buildInitializationContext().
|
|
3801
|
-
|
|
3802
|
-
|
|
3803
|
-
type:
|
|
3804
|
-
reason:
|
|
3805
|
-
attempt:
|
|
3806
|
-
attempts:
|
|
3565
|
+
z23.object({ type: z23.literal("session_manifest") }).passthrough(),
|
|
3566
|
+
z23.object({
|
|
3567
|
+
type: z23.literal("agent_runner_status"),
|
|
3568
|
+
reason: z23.string(),
|
|
3569
|
+
attempt: z23.number().optional(),
|
|
3570
|
+
attempts: z23.number().optional()
|
|
3807
3571
|
}).passthrough(),
|
|
3808
|
-
|
|
3809
|
-
|
|
3810
|
-
|
|
3572
|
+
z23.object({ type: z23.literal("shutdown"), reason: z23.string().optional() }).passthrough(),
|
|
3573
|
+
z23.object({ type: z23.literal("mode_changed"), agentMode: z23.string() }).passthrough(),
|
|
3574
|
+
z23.object({ type: z23.literal("mode_transition"), from: z23.string(), to: z23.string() }).passthrough(),
|
|
3811
3575
|
// ── Turn stream ───────────────────────────────────────────────────────
|
|
3812
|
-
|
|
3813
|
-
|
|
3814
|
-
|
|
3815
|
-
type:
|
|
3816
|
-
tool:
|
|
3576
|
+
z23.object({ type: z23.literal("message"), content: z23.string() }).passthrough(),
|
|
3577
|
+
z23.object({ type: z23.literal("thinking"), message: z23.string() }).passthrough(),
|
|
3578
|
+
z23.object({
|
|
3579
|
+
type: z23.literal("tool_use"),
|
|
3580
|
+
tool: z23.string(),
|
|
3817
3581
|
// Producers send JSON.stringify(input); consumers defend against
|
|
3818
3582
|
// object inputs from older agents, so the wire stays permissive here.
|
|
3819
|
-
input:
|
|
3583
|
+
input: z23.unknown().optional()
|
|
3820
3584
|
}).passthrough(),
|
|
3821
|
-
|
|
3822
|
-
type:
|
|
3823
|
-
tool:
|
|
3824
|
-
output:
|
|
3825
|
-
isError:
|
|
3826
|
-
redactedCount:
|
|
3585
|
+
z23.object({
|
|
3586
|
+
type: z23.literal("tool_result"),
|
|
3587
|
+
tool: z23.string(),
|
|
3588
|
+
output: z23.unknown().optional(),
|
|
3589
|
+
isError: z23.boolean().optional(),
|
|
3590
|
+
redactedCount: z23.number().optional()
|
|
3827
3591
|
}).passthrough(),
|
|
3828
|
-
|
|
3829
|
-
|
|
3830
|
-
type:
|
|
3831
|
-
summary:
|
|
3832
|
-
durationMs:
|
|
3592
|
+
z23.object({ type: z23.literal("turn_end"), toolCalls: z23.array(TurnEndToolCallSchema) }).passthrough(),
|
|
3593
|
+
z23.object({
|
|
3594
|
+
type: z23.literal("completed"),
|
|
3595
|
+
summary: z23.string().optional(),
|
|
3596
|
+
durationMs: z23.number().optional(),
|
|
3833
3597
|
/** Tokens this turn consumed, summed from the transcript's assistant
|
|
3834
3598
|
* records. Optional: an older agent build sends none, and the server
|
|
3835
3599
|
* then records nothing rather than zeroing the session's totals. */
|
|
3836
3600
|
usage: AgentTurnUsageSchema.optional()
|
|
3837
3601
|
}).passthrough(),
|
|
3838
|
-
|
|
3839
|
-
|
|
3840
|
-
|
|
3602
|
+
z23.object({ type: z23.literal("error"), message: z23.string() }).passthrough(),
|
|
3603
|
+
z23.object({ type: z23.literal("agent_typing_start") }).passthrough(),
|
|
3604
|
+
z23.object({ type: z23.literal("agent_typing_stop") }).passthrough(),
|
|
3841
3605
|
// ── Telemetry ─────────────────────────────────────────────────────────
|
|
3842
3606
|
// heartbeat/typing: legacy telemetry the server still classifies as
|
|
3843
3607
|
// transient (TRANSIENT_EVENT_TYPES) — kept in the vocabulary.
|
|
3844
|
-
|
|
3845
|
-
|
|
3846
|
-
|
|
3847
|
-
type:
|
|
3848
|
-
contextTokens:
|
|
3849
|
-
contextWindow:
|
|
3850
|
-
inputTokens:
|
|
3851
|
-
cacheReadInputTokens:
|
|
3852
|
-
cacheCreationInputTokens:
|
|
3853
|
-
totalTokensUsed:
|
|
3608
|
+
z23.object({ type: z23.literal("heartbeat") }).passthrough(),
|
|
3609
|
+
z23.object({ type: z23.literal("typing") }).passthrough(),
|
|
3610
|
+
z23.object({
|
|
3611
|
+
type: z23.literal("context_update"),
|
|
3612
|
+
contextTokens: z23.number(),
|
|
3613
|
+
contextWindow: z23.number(),
|
|
3614
|
+
inputTokens: z23.number().optional(),
|
|
3615
|
+
cacheReadInputTokens: z23.number().optional(),
|
|
3616
|
+
cacheCreationInputTokens: z23.number().optional(),
|
|
3617
|
+
totalTokensUsed: z23.number().optional()
|
|
3854
3618
|
}).passthrough(),
|
|
3855
3619
|
// Four producer shapes share this type: {rateLimitType, utilization, status}
|
|
3856
3620
|
// (SDK rate_limit_event), {resetsAt} (agent-connection resume notice), the
|
|
3857
3621
|
// usage-sampler ({rateLimitType, utilization, status, resetsAt, gauges}
|
|
3858
3622
|
// — resetsAt matches rateLimitType; gauges survives via .passthrough()), and
|
|
3859
3623
|
// {unmeasurable, reason} (the sampler reporting it cannot read this key).
|
|
3860
|
-
|
|
3861
|
-
type:
|
|
3862
|
-
rateLimitType:
|
|
3863
|
-
utilization:
|
|
3864
|
-
status:
|
|
3865
|
-
resetsAt:
|
|
3866
|
-
unmeasurable:
|
|
3867
|
-
reason:
|
|
3624
|
+
z23.object({
|
|
3625
|
+
type: z23.literal("rate_limit_update"),
|
|
3626
|
+
rateLimitType: z23.string().optional(),
|
|
3627
|
+
utilization: z23.number().optional(),
|
|
3628
|
+
status: z23.string().optional(),
|
|
3629
|
+
resetsAt: z23.string().optional(),
|
|
3630
|
+
unmeasurable: z23.boolean().optional(),
|
|
3631
|
+
reason: z23.string().optional()
|
|
3868
3632
|
}).passthrough(),
|
|
3869
|
-
|
|
3870
|
-
type:
|
|
3871
|
-
trigger:
|
|
3872
|
-
preTokens:
|
|
3633
|
+
z23.object({
|
|
3634
|
+
type: z23.literal("context_compacted"),
|
|
3635
|
+
trigger: z23.string().optional(),
|
|
3636
|
+
preTokens: z23.number().optional()
|
|
3873
3637
|
}).passthrough(),
|
|
3874
|
-
|
|
3875
|
-
type:
|
|
3876
|
-
toolName:
|
|
3877
|
-
elapsedSeconds:
|
|
3638
|
+
z23.object({
|
|
3639
|
+
type: z23.literal("tool_progress"),
|
|
3640
|
+
toolName: z23.string().optional(),
|
|
3641
|
+
elapsedSeconds: z23.number().optional()
|
|
3878
3642
|
}).passthrough(),
|
|
3879
|
-
|
|
3880
|
-
type:
|
|
3881
|
-
sdkTaskId:
|
|
3882
|
-
description:
|
|
3643
|
+
z23.object({
|
|
3644
|
+
type: z23.literal("subagent_started"),
|
|
3645
|
+
sdkTaskId: z23.string().optional(),
|
|
3646
|
+
description: z23.string().optional()
|
|
3883
3647
|
}).passthrough(),
|
|
3884
|
-
|
|
3885
|
-
type:
|
|
3886
|
-
sdkTaskId:
|
|
3887
|
-
description:
|
|
3888
|
-
toolUses:
|
|
3889
|
-
durationMs:
|
|
3648
|
+
z23.object({
|
|
3649
|
+
type: z23.literal("subagent_progress"),
|
|
3650
|
+
sdkTaskId: z23.string().optional(),
|
|
3651
|
+
description: z23.string().optional(),
|
|
3652
|
+
toolUses: z23.number().optional(),
|
|
3653
|
+
durationMs: z23.number().optional()
|
|
3890
3654
|
}).passthrough(),
|
|
3891
3655
|
// ── Work products ─────────────────────────────────────────────────────
|
|
3892
|
-
|
|
3893
|
-
|
|
3894
|
-
type:
|
|
3895
|
-
result:
|
|
3896
|
-
summary:
|
|
3897
|
-
issues:
|
|
3898
|
-
|
|
3899
|
-
file:
|
|
3900
|
-
line:
|
|
3901
|
-
severity:
|
|
3902
|
-
description:
|
|
3656
|
+
z23.object({ type: z23.literal("pr_created"), url: z23.string(), number: z23.number() }).passthrough(),
|
|
3657
|
+
z23.object({
|
|
3658
|
+
type: z23.literal("code_review_complete"),
|
|
3659
|
+
result: z23.enum(["approved", "changes_requested"]),
|
|
3660
|
+
summary: z23.string().optional(),
|
|
3661
|
+
issues: z23.array(
|
|
3662
|
+
z23.object({
|
|
3663
|
+
file: z23.string(),
|
|
3664
|
+
line: z23.number().optional(),
|
|
3665
|
+
severity: z23.string().optional(),
|
|
3666
|
+
description: z23.string().optional()
|
|
3903
3667
|
}).passthrough()
|
|
3904
3668
|
).optional()
|
|
3905
3669
|
}).passthrough(),
|
|
3906
3670
|
// ── Environment setup / start command ─────────────────────────────────
|
|
3907
|
-
|
|
3908
|
-
|
|
3909
|
-
type:
|
|
3910
|
-
startCommandRunning:
|
|
3911
|
-
startCommandConfigured:
|
|
3671
|
+
z23.object({ type: z23.literal("setup_output"), stream: z23.string(), data: z23.string() }).passthrough(),
|
|
3672
|
+
z23.object({
|
|
3673
|
+
type: z23.literal("setup_complete"),
|
|
3674
|
+
startCommandRunning: z23.boolean().optional(),
|
|
3675
|
+
startCommandConfigured: z23.boolean().optional(),
|
|
3912
3676
|
// Sanitized server-side by sanitizeSessionPreviewPorts — stays unknown.
|
|
3913
|
-
previewPorts:
|
|
3677
|
+
previewPorts: z23.unknown().optional(),
|
|
3678
|
+
// Sanitized server-side by sanitizeSessionPreviewLinks — stays unknown.
|
|
3679
|
+
previewLinks: z23.unknown().optional()
|
|
3914
3680
|
}).passthrough(),
|
|
3915
|
-
|
|
3916
|
-
|
|
3917
|
-
|
|
3918
|
-
|
|
3919
|
-
type:
|
|
3920
|
-
code:
|
|
3921
|
-
signal:
|
|
3922
|
-
message:
|
|
3681
|
+
z23.object({ type: z23.literal("setup_error"), message: z23.string() }).passthrough(),
|
|
3682
|
+
z23.object({ type: z23.literal("start_command_started") }).passthrough(),
|
|
3683
|
+
z23.object({ type: z23.literal("start_command_output"), stream: z23.string(), data: z23.string() }).passthrough(),
|
|
3684
|
+
z23.object({
|
|
3685
|
+
type: z23.literal("start_command_exited"),
|
|
3686
|
+
code: z23.number().nullable().optional(),
|
|
3687
|
+
signal: z23.string().nullable().optional(),
|
|
3688
|
+
message: z23.string().optional()
|
|
3923
3689
|
}).passthrough(),
|
|
3924
|
-
|
|
3690
|
+
z23.object({ type: z23.literal("start_command_error"), message: z23.string() }).passthrough()
|
|
3925
3691
|
]);
|
|
3926
|
-
var AgentEventSchema =
|
|
3692
|
+
var AgentEventSchema = z23.union([
|
|
3927
3693
|
KnownAgentEventSchema,
|
|
3928
|
-
|
|
3694
|
+
z23.object({ type: z23.string().min(1) }).catchall(z23.unknown())
|
|
3929
3695
|
]);
|
|
3930
3696
|
var cardDescription = z32.string().max(CARD_DESCRIPTION_MAX, CARD_DESCRIPTION_LIMIT_MESSAGE).optional();
|
|
3931
3697
|
var AgentHeartbeatSchema = z32.object({
|
|
@@ -4042,7 +3808,7 @@ var StoreSessionIdRequestSchema = z32.object({
|
|
|
4042
3808
|
});
|
|
4043
3809
|
var SetManualTestsRequestSchema = z32.object({
|
|
4044
3810
|
sessionId: z32.string(),
|
|
4045
|
-
items:
|
|
3811
|
+
items: ManualTestItemsSchema
|
|
4046
3812
|
});
|
|
4047
3813
|
var EditManualTestRequestSchema = z32.object({
|
|
4048
3814
|
sessionId: z32.string(),
|
|
@@ -4388,6 +4154,9 @@ var ReportPtyStreamRequestSchema = z32.object({
|
|
|
4388
4154
|
sessionId: z32.string(),
|
|
4389
4155
|
port: z32.number().int().positive().max(65535).nullable()
|
|
4390
4156
|
});
|
|
4157
|
+
var ReportPtyInputRequestSchema = z32.object({
|
|
4158
|
+
sessionId: z32.string()
|
|
4159
|
+
});
|
|
4391
4160
|
var GetPtyStreamEndpointRequestSchema = z32.object({
|
|
4392
4161
|
sessionId: z32.string()
|
|
4393
4162
|
});
|
|
@@ -4505,6 +4274,11 @@ var ListProjectTasksRequestSchema = z52.object({
|
|
|
4505
4274
|
// Scope to a sub-project board when provided. Unlike the board layer's `?? null`
|
|
4506
4275
|
// semantics, agents default to seeing the whole project when omitted.
|
|
4507
4276
|
subProjectId: z52.string().nullable().optional(),
|
|
4277
|
+
// Plain equality on the on-hold flag: true = only parked cards, false =
|
|
4278
|
+
// exclude them, omitted = both. Deliberately NOT the board's
|
|
4279
|
+
// includeOnHold/surfaceActiveWork bucketing — an agent sweeping a backlog
|
|
4280
|
+
// needs the raw column, not a column projection.
|
|
4281
|
+
onHold: z52.boolean().optional(),
|
|
4508
4282
|
limit: z52.number().int().positive().optional().default(50)
|
|
4509
4283
|
}).refine((p) => !(p.unassigned && p.assigneeId), {
|
|
4510
4284
|
message: "Pass either assigneeId or unassigned, not both"
|
|
@@ -4539,7 +4313,9 @@ var SearchProjectTasksRequestSchema = z52.object({
|
|
|
4539
4313
|
message: "Pass either assigneeId or unassigned, not both"
|
|
4540
4314
|
});
|
|
4541
4315
|
var ListProjectTagsRequestSchema = z52.object({
|
|
4542
|
-
projectId: z52.string()
|
|
4316
|
+
projectId: z52.string(),
|
|
4317
|
+
/** "summary" (default) omits inline context links; "full" includes them. */
|
|
4318
|
+
detail: z52.enum(["summary", "full"]).optional()
|
|
4543
4319
|
});
|
|
4544
4320
|
var GetProjectTagRequestSchema = z52.object({
|
|
4545
4321
|
projectId: z52.string(),
|
|
@@ -4642,11 +4418,15 @@ var UpdateProjectTaskRequestSchema = z52.object({
|
|
|
4642
4418
|
// handler: the ref must exist on origin and no live workspace may be bound
|
|
4643
4419
|
// to a different branch.
|
|
4644
4420
|
githubBranch: z52.string().min(1).max(GIT_BRANCH_NAME_MAX).refine(isValidGitBranchName, { message: GIT_BRANCH_NAME_MESSAGE }).nullable().optional(),
|
|
4421
|
+
// Park the card on hold (true) or return it to the active board (false).
|
|
4422
|
+
// The handler mirrors the web path's two side effects: a board re-bucket
|
|
4423
|
+
// event, and demotion to Open when an active-work card is parked.
|
|
4424
|
+
onHold: z52.boolean().optional(),
|
|
4645
4425
|
requestingUserId: z52.string().optional()
|
|
4646
4426
|
}).strict().refine(
|
|
4647
|
-
(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,
|
|
4427
|
+
(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,
|
|
4648
4428
|
{
|
|
4649
|
-
message: "update_task requires at least one field to change (title, description, plan, status, risk, storyPointValue, assignedUserId, subProjectId, or
|
|
4429
|
+
message: "update_task requires at least one field to change (title, description, plan, status, risk, storyPointValue, assignedUserId, subProjectId, githubBranch, or onHold)"
|
|
4650
4430
|
}
|
|
4651
4431
|
);
|
|
4652
4432
|
var TransitionProjectTaskStatusRequestSchema = z52.object({
|
|
@@ -4967,7 +4747,7 @@ var QueryProjectManualTestsRequestSchema = z52.object({
|
|
|
4967
4747
|
var SetProjectManualTestsRequestSchema = z52.object({
|
|
4968
4748
|
projectId: z52.string(),
|
|
4969
4749
|
taskId: z52.string(),
|
|
4970
|
-
items:
|
|
4750
|
+
items: ManualTestItemsSchema,
|
|
4971
4751
|
requestingUserId: z52.string().optional()
|
|
4972
4752
|
});
|
|
4973
4753
|
var EditProjectManualTestRequestSchema = z52.object({
|
|
@@ -5046,170 +4826,134 @@ var ReportApiOutageRequestSchema = z72.object({
|
|
|
5046
4826
|
detail: z72.string().max(2e3),
|
|
5047
4827
|
attempts: z72.number().int().min(0).max(100)
|
|
5048
4828
|
});
|
|
5049
|
-
var SHA_PATTERN = /^[0-9a-f]{40}$/i;
|
|
5050
|
-
var ReviewGuideFileReferenceSchema = z82.object({
|
|
5051
|
-
path: z82.string().min(1).max(500),
|
|
5052
|
-
startLine: z82.number().int().positive().max(1e6).optional(),
|
|
5053
|
-
endLine: z82.number().int().positive().max(1e6).optional(),
|
|
5054
|
-
hunkHeader: z82.string().min(1).max(300).optional()
|
|
5055
|
-
}).strict().superRefine((value, ctx) => {
|
|
5056
|
-
if (value.endLine !== void 0 && value.startLine === void 0) {
|
|
5057
|
-
ctx.addIssue({
|
|
5058
|
-
code: "custom",
|
|
5059
|
-
path: ["startLine"],
|
|
5060
|
-
message: "startLine is required when endLine is set"
|
|
5061
|
-
});
|
|
5062
|
-
}
|
|
5063
|
-
if (value.startLine !== void 0 && value.endLine !== void 0 && value.endLine < value.startLine) {
|
|
5064
|
-
ctx.addIssue({
|
|
5065
|
-
code: "custom",
|
|
5066
|
-
path: ["endLine"],
|
|
5067
|
-
message: "endLine must be greater than or equal to startLine"
|
|
5068
|
-
});
|
|
5069
|
-
}
|
|
5070
|
-
});
|
|
5071
|
-
var ReviewGuideSectionSchema = z82.object({
|
|
5072
|
-
title: z82.string().min(1).max(160),
|
|
5073
|
-
explanation: z82.string().min(1).max(2e3),
|
|
5074
|
-
classification: z82.enum(["core", "supporting"]).optional(),
|
|
5075
|
-
files: z82.array(ReviewGuideFileReferenceSchema).min(1).max(20)
|
|
5076
|
-
}).strict();
|
|
5077
|
-
var ReviewGuideContentSchema = z82.object({
|
|
5078
|
-
overview: z82.string().min(1).max(3e3),
|
|
5079
|
-
sections: z82.array(ReviewGuideSectionSchema).min(1).max(12)
|
|
5080
|
-
}).strict();
|
|
5081
|
-
var PublishReviewGuideRequestSchema = ReviewGuideContentSchema.extend({
|
|
5082
|
-
sessionId: z82.string().min(1),
|
|
5083
|
-
reviewedSha: z82.string().regex(SHA_PATTERN, "reviewedSha must be a 40-character commit SHA")
|
|
5084
|
-
}).strict();
|
|
5085
4829
|
var CONTEXT_LINK_LOCATOR_MAX2 = 300;
|
|
5086
4830
|
var TAG_DESCRIPTION_MAX = CARD_DESCRIPTION_MAX;
|
|
5087
4831
|
var TAG_OVERVIEW_MAX = 32e3;
|
|
5088
4832
|
var TAG_REASON_MAX = 500;
|
|
5089
|
-
var ProjectTagContextPathSchema =
|
|
5090
|
-
type:
|
|
5091
|
-
path:
|
|
5092
|
-
label:
|
|
4833
|
+
var ProjectTagContextPathSchema = z82.object({
|
|
4834
|
+
type: z82.enum(["rule", "doc", "file", "folder"]),
|
|
4835
|
+
path: z82.string().min(1).max(500),
|
|
4836
|
+
label: z82.string().max(100).optional(),
|
|
5093
4837
|
/** Verified-link tether — text that must keep existing in the file. */
|
|
5094
|
-
locator:
|
|
4838
|
+
locator: z82.string().min(1).max(CONTEXT_LINK_LOCATOR_MAX2).regex(/^[^\r\n]*$/, "Locator cannot contain line breaks").optional(),
|
|
5095
4839
|
/** test = must appear in a real test/describe title; code = any substring. */
|
|
5096
|
-
locatorType:
|
|
4840
|
+
locatorType: z82.enum(["test", "code"]).optional()
|
|
5097
4841
|
}).refine((link) => link.locator === void 0 === (link.locatorType === void 0), {
|
|
5098
4842
|
message: "locator and locatorType must be provided together"
|
|
5099
4843
|
}).refine((link) => link.locator === void 0 || link.type !== "folder", {
|
|
5100
4844
|
message: "folder links cannot carry a locator"
|
|
5101
4845
|
});
|
|
5102
|
-
var hexColor =
|
|
5103
|
-
var overviewPathSchema =
|
|
5104
|
-
var CreateProjectTagRequestSchema =
|
|
5105
|
-
projectId:
|
|
5106
|
-
name:
|
|
4846
|
+
var hexColor = z82.string().regex(/^#[0-9a-fA-F]{6}$/, "Expected #RRGGBB hex color");
|
|
4847
|
+
var overviewPathSchema = z82.string().min(1).max(500).regex(/^[^\r\n]*$/, "Overview path cannot contain line breaks");
|
|
4848
|
+
var CreateProjectTagRequestSchema = z82.object({
|
|
4849
|
+
projectId: z82.string(),
|
|
4850
|
+
name: z82.string().min(1).max(50),
|
|
5107
4851
|
color: hexColor.optional(),
|
|
5108
|
-
description:
|
|
5109
|
-
overview:
|
|
4852
|
+
description: z82.string().max(TAG_DESCRIPTION_MAX).optional(),
|
|
4853
|
+
overview: z82.string().max(TAG_OVERVIEW_MAX).optional(),
|
|
5110
4854
|
/** Source the overview from this repo file (stored overview stays as the pending fallback). */
|
|
5111
4855
|
overviewPath: overviewPathSchema.optional(),
|
|
5112
|
-
contextPaths:
|
|
4856
|
+
contextPaths: z82.array(ProjectTagContextPathSchema).max(20).optional(),
|
|
5113
4857
|
/** Parents to link at create time (multi-parent DAG). */
|
|
5114
|
-
parentTagIds:
|
|
5115
|
-
requestingUserId:
|
|
4858
|
+
parentTagIds: z82.array(z82.string()).max(25).optional(),
|
|
4859
|
+
requestingUserId: z82.string().optional()
|
|
5116
4860
|
});
|
|
5117
|
-
var UpdateProjectTagRequestSchema =
|
|
5118
|
-
projectId:
|
|
5119
|
-
tagId:
|
|
5120
|
-
name:
|
|
4861
|
+
var UpdateProjectTagRequestSchema = z82.object({
|
|
4862
|
+
projectId: z82.string(),
|
|
4863
|
+
tagId: z82.string(),
|
|
4864
|
+
name: z82.string().min(1).max(50).optional(),
|
|
5121
4865
|
color: hexColor.optional(),
|
|
5122
|
-
description:
|
|
4866
|
+
description: z82.string().max(TAG_DESCRIPTION_MAX).optional(),
|
|
5123
4867
|
/** Full markdown glossary body; null clears it. Rejected while overviewPath is set. */
|
|
5124
|
-
overview:
|
|
4868
|
+
overview: z82.string().max(TAG_OVERVIEW_MAX).nullable().optional(),
|
|
5125
4869
|
/** Repo file to source the overview from; null clears back to the stored overview. */
|
|
5126
4870
|
overviewPath: overviewPathSchema.nullable().optional(),
|
|
5127
4871
|
/** Full replacement of the tag's context links when provided. */
|
|
5128
|
-
contextPaths:
|
|
4872
|
+
contextPaths: z82.array(ProjectTagContextPathSchema).max(20).optional(),
|
|
5129
4873
|
/** Full-set replacement of the tag's parent tags (multi-parent DAG). */
|
|
5130
|
-
parentTagIds:
|
|
4874
|
+
parentTagIds: z82.array(z82.string()).max(25).optional(),
|
|
5131
4875
|
/** One-line revision provenance, recorded in the tag's history. */
|
|
5132
|
-
reason:
|
|
4876
|
+
reason: z82.string().max(TAG_REASON_MAX).optional(),
|
|
5133
4877
|
/** Card the caller was working in — stamped into the revision history. */
|
|
5134
|
-
taskId:
|
|
5135
|
-
requestingUserId:
|
|
4878
|
+
taskId: z82.string().optional(),
|
|
4879
|
+
requestingUserId: z82.string().optional()
|
|
5136
4880
|
});
|
|
5137
|
-
var PostToProjectChatRequestSchema =
|
|
5138
|
-
projectId:
|
|
5139
|
-
content:
|
|
5140
|
-
requestingUserId:
|
|
4881
|
+
var PostToProjectChatRequestSchema = z82.object({
|
|
4882
|
+
projectId: z82.string(),
|
|
4883
|
+
content: z82.string().min(1).max(2e4),
|
|
4884
|
+
requestingUserId: z82.string().optional(),
|
|
5141
4885
|
/** Marks the post so the server can persist it beyond chat (tag-audit summaries land in tag history). */
|
|
5142
|
-
kind:
|
|
5143
|
-
});
|
|
5144
|
-
var StartTagAuditRequestSchema =
|
|
5145
|
-
projectId:
|
|
5146
|
-
requestingUserId:
|
|
5147
|
-
});
|
|
5148
|
-
var StartTaskAuditRequestSchema =
|
|
5149
|
-
projectId:
|
|
5150
|
-
taskIds:
|
|
5151
|
-
requestingUserId:
|
|
5152
|
-
});
|
|
5153
|
-
var GetActiveAuditSessionsRequestSchema =
|
|
5154
|
-
projectId:
|
|
5155
|
-
});
|
|
5156
|
-
var ReportTaskAuditResultRequestSchema =
|
|
5157
|
-
projectId:
|
|
5158
|
-
taskId:
|
|
5159
|
-
summary:
|
|
5160
|
-
turnGrades:
|
|
5161
|
-
|
|
5162
|
-
turnIndex:
|
|
5163
|
-
phase:
|
|
5164
|
-
grade:
|
|
5165
|
-
reasoning:
|
|
5166
|
-
eventType:
|
|
5167
|
-
eventSummary:
|
|
4886
|
+
kind: z82.enum(["tag_audit_summary"]).optional()
|
|
4887
|
+
});
|
|
4888
|
+
var StartTagAuditRequestSchema = z82.object({
|
|
4889
|
+
projectId: z82.string(),
|
|
4890
|
+
requestingUserId: z82.string().optional()
|
|
4891
|
+
});
|
|
4892
|
+
var StartTaskAuditRequestSchema = z82.object({
|
|
4893
|
+
projectId: z82.string(),
|
|
4894
|
+
taskIds: z82.array(z82.string()).min(1).max(20),
|
|
4895
|
+
requestingUserId: z82.string().optional()
|
|
4896
|
+
});
|
|
4897
|
+
var GetActiveAuditSessionsRequestSchema = z82.object({
|
|
4898
|
+
projectId: z82.string()
|
|
4899
|
+
});
|
|
4900
|
+
var ReportTaskAuditResultRequestSchema = z82.object({
|
|
4901
|
+
projectId: z82.string(),
|
|
4902
|
+
taskId: z82.string(),
|
|
4903
|
+
summary: z82.string(),
|
|
4904
|
+
turnGrades: z82.array(
|
|
4905
|
+
z82.object({
|
|
4906
|
+
turnIndex: z82.number(),
|
|
4907
|
+
phase: z82.enum(["planning", "building", "human"]),
|
|
4908
|
+
grade: z82.enum(["correct", "neutral", "blunder"]),
|
|
4909
|
+
reasoning: z82.string(),
|
|
4910
|
+
eventType: z82.string(),
|
|
4911
|
+
eventSummary: z82.string()
|
|
5168
4912
|
})
|
|
5169
4913
|
),
|
|
5170
|
-
planningAccuracy:
|
|
5171
|
-
buildingAccuracy:
|
|
5172
|
-
humanAccuracy:
|
|
5173
|
-
planningCorrect:
|
|
5174
|
-
planningNeutral:
|
|
5175
|
-
planningBlunder:
|
|
5176
|
-
buildingCorrect:
|
|
5177
|
-
buildingNeutral:
|
|
5178
|
-
buildingBlunder:
|
|
5179
|
-
humanCorrect:
|
|
5180
|
-
humanNeutral:
|
|
5181
|
-
humanBlunder:
|
|
5182
|
-
humanEvaluations:
|
|
5183
|
-
|
|
5184
|
-
messageIndex:
|
|
5185
|
-
rating:
|
|
5186
|
-
reasoning:
|
|
4914
|
+
planningAccuracy: z82.number().nullable(),
|
|
4915
|
+
buildingAccuracy: z82.number().nullable(),
|
|
4916
|
+
humanAccuracy: z82.number().nullable(),
|
|
4917
|
+
planningCorrect: z82.number(),
|
|
4918
|
+
planningNeutral: z82.number(),
|
|
4919
|
+
planningBlunder: z82.number(),
|
|
4920
|
+
buildingCorrect: z82.number(),
|
|
4921
|
+
buildingNeutral: z82.number(),
|
|
4922
|
+
buildingBlunder: z82.number(),
|
|
4923
|
+
humanCorrect: z82.number(),
|
|
4924
|
+
humanNeutral: z82.number(),
|
|
4925
|
+
humanBlunder: z82.number(),
|
|
4926
|
+
humanEvaluations: z82.array(
|
|
4927
|
+
z82.object({
|
|
4928
|
+
messageIndex: z82.number(),
|
|
4929
|
+
rating: z82.union([z82.literal(-1), z82.literal(0), z82.literal(1)]),
|
|
4930
|
+
reasoning: z82.string()
|
|
5187
4931
|
})
|
|
5188
4932
|
).optional(),
|
|
5189
|
-
suggestionIds:
|
|
5190
|
-
auditCostUsd:
|
|
5191
|
-
model:
|
|
4933
|
+
suggestionIds: z82.array(z82.string()),
|
|
4934
|
+
auditCostUsd: z82.number().nullable(),
|
|
4935
|
+
model: z82.string().nullable(),
|
|
5192
4936
|
/** When set, the audit is marked failed with this message instead. */
|
|
5193
|
-
error:
|
|
4937
|
+
error: z82.string().optional()
|
|
5194
4938
|
});
|
|
5195
|
-
var GetTaskAuditsRequestSchema =
|
|
5196
|
-
projectId:
|
|
5197
|
-
limit:
|
|
4939
|
+
var GetTaskAuditsRequestSchema = z82.object({
|
|
4940
|
+
projectId: z82.string(),
|
|
4941
|
+
limit: z82.number().int().positive().max(200).optional().default(50)
|
|
5198
4942
|
});
|
|
5199
|
-
var GetTaskAuditRequestSchema =
|
|
5200
|
-
projectId:
|
|
5201
|
-
auditId:
|
|
4943
|
+
var GetTaskAuditRequestSchema = z82.object({
|
|
4944
|
+
projectId: z82.string(),
|
|
4945
|
+
auditId: z82.string()
|
|
5202
4946
|
});
|
|
5203
|
-
var GetTaskAuditAggregatesRequestSchema =
|
|
5204
|
-
projectId:
|
|
4947
|
+
var GetTaskAuditAggregatesRequestSchema = z82.object({
|
|
4948
|
+
projectId: z82.string()
|
|
5205
4949
|
});
|
|
5206
|
-
var DeleteTaskAuditRequestSchema =
|
|
5207
|
-
projectId:
|
|
5208
|
-
auditId:
|
|
5209
|
-
requestingUserId:
|
|
4950
|
+
var DeleteTaskAuditRequestSchema = z82.object({
|
|
4951
|
+
projectId: z82.string(),
|
|
4952
|
+
auditId: z82.string(),
|
|
4953
|
+
requestingUserId: z82.string().optional()
|
|
5210
4954
|
});
|
|
5211
|
-
var MarkInitialPromptSubmittedRequestSchema =
|
|
5212
|
-
sessionId:
|
|
4955
|
+
var MarkInitialPromptSubmittedRequestSchema = z82.object({
|
|
4956
|
+
sessionId: z82.string()
|
|
5213
4957
|
});
|
|
5214
4958
|
var DECISION_STATUS_LABELS = ["Open", "Decided", "Cancelled"];
|
|
5215
4959
|
var DECISION_OPTION_KINDS = ["current", "planned", "proposed"];
|
|
@@ -5224,166 +4968,265 @@ var DECISION_DEADLINE_MIN_HOURS = 1;
|
|
|
5224
4968
|
var DECISION_DEADLINE_MAX_DAYS = 30;
|
|
5225
4969
|
var DECISION_DEADLINE_MAX_HOURS = DECISION_DEADLINE_MAX_DAYS * 24;
|
|
5226
4970
|
var DECISION_LIST_MAX_LIMIT = 200;
|
|
5227
|
-
var decisionIdSchema =
|
|
5228
|
-
var optionInputSchema =
|
|
5229
|
-
label:
|
|
5230
|
-
description:
|
|
5231
|
-
kind:
|
|
4971
|
+
var decisionIdSchema = z92.string().cuid("Invalid decision ID");
|
|
4972
|
+
var optionInputSchema = z92.object({
|
|
4973
|
+
label: z92.string().min(1, "Option label is required").max(DECISION_OPTION_LABEL_MAX),
|
|
4974
|
+
description: z92.string().min(1, "Describe the option as GIVEN / WHEN / THEN"),
|
|
4975
|
+
kind: z92.enum(DECISION_OPTION_KINDS).optional()
|
|
5232
4976
|
});
|
|
5233
|
-
var optionsArraySchema =
|
|
4977
|
+
var optionsArraySchema = z92.array(optionInputSchema).min(DECISION_OPTIONS_MIN, `A decision needs at least ${DECISION_OPTIONS_MIN} options`).max(
|
|
5234
4978
|
DECISION_OPTIONS_MAX,
|
|
5235
4979
|
`A decision takes at most ${DECISION_OPTIONS_MAX} options \u2014 more than that means the question is not framed yet`
|
|
5236
4980
|
);
|
|
5237
|
-
var CreateDecisionRequestSchema =
|
|
5238
|
-
projectId:
|
|
4981
|
+
var CreateDecisionRequestSchema = z92.object({
|
|
4982
|
+
projectId: z92.string().cuid("Invalid project ID"),
|
|
5239
4983
|
/** The question, as one plain sentence. */
|
|
5240
|
-
title:
|
|
5241
|
-
description:
|
|
5242
|
-
context:
|
|
4984
|
+
title: z92.string().min(1, "A decision needs a question").max(DECISION_QUESTION_MAX),
|
|
4985
|
+
description: z92.string().max(CARD_DESCRIPTION_MAX).optional(),
|
|
4986
|
+
context: z92.string().min(1, "Context is required \u2014 say why this is being asked now"),
|
|
5243
4987
|
options: optionsArraySchema,
|
|
5244
4988
|
/** 1-based position of the option that wins on silence. */
|
|
5245
|
-
defaultOptionOrdinal:
|
|
5246
|
-
deadlineHours:
|
|
4989
|
+
defaultOptionOrdinal: z92.number().int().min(1).max(DECISION_OPTIONS_MAX),
|
|
4990
|
+
deadlineHours: z92.number().min(DECISION_DEADLINE_MIN_HOURS).max(DECISION_DEADLINE_MAX_HOURS).optional(),
|
|
5247
4991
|
/** Cards this decision blocks, by cuid or slug. */
|
|
5248
|
-
impactedCardRefs:
|
|
5249
|
-
tagNames:
|
|
5250
|
-
raisedByTaskId:
|
|
5251
|
-
raisedByPrNumber:
|
|
4992
|
+
impactedCardRefs: z92.array(z92.string().min(1)).max(50).optional(),
|
|
4993
|
+
tagNames: z92.array(z92.string().min(1)).max(10).optional(),
|
|
4994
|
+
raisedByTaskId: z92.string().min(1).optional(),
|
|
4995
|
+
raisedByPrNumber: z92.number().int().positive().optional()
|
|
5252
4996
|
});
|
|
5253
|
-
var VoteDecisionRequestSchema =
|
|
4997
|
+
var VoteDecisionRequestSchema = z92.object({
|
|
5254
4998
|
decisionId: decisionIdSchema,
|
|
5255
|
-
optionId:
|
|
5256
|
-
reason:
|
|
5257
|
-
source:
|
|
4999
|
+
optionId: z92.string().cuid("Invalid option ID"),
|
|
5000
|
+
reason: z92.string().max(DECISION_VOTE_REASON_MAX).optional(),
|
|
5001
|
+
source: z92.enum(DECISION_VOTE_SOURCES).optional()
|
|
5258
5002
|
});
|
|
5259
|
-
var RemoveDecisionVoteRequestSchema =
|
|
5003
|
+
var RemoveDecisionVoteRequestSchema = z92.object({
|
|
5260
5004
|
decisionId: decisionIdSchema
|
|
5261
5005
|
});
|
|
5262
|
-
var ResolveDecisionRequestSchema =
|
|
5006
|
+
var ResolveDecisionRequestSchema = z92.object({
|
|
5263
5007
|
decisionId: decisionIdSchema,
|
|
5264
|
-
chosenOptionId:
|
|
5265
|
-
resolution:
|
|
5266
|
-
resolutionNotes:
|
|
5008
|
+
chosenOptionId: z92.string().cuid("Invalid option ID"),
|
|
5009
|
+
resolution: z92.string().min(1, "A resolution is required \u2014 say what was decided").max(DECISION_RESOLUTION_MAX),
|
|
5010
|
+
resolutionNotes: z92.string().optional()
|
|
5267
5011
|
});
|
|
5268
|
-
var CancelDecisionRequestSchema =
|
|
5012
|
+
var CancelDecisionRequestSchema = z92.object({
|
|
5269
5013
|
decisionId: decisionIdSchema,
|
|
5270
5014
|
/** Why it is being dropped. Posted to the decision thread. */
|
|
5271
|
-
reason:
|
|
5015
|
+
reason: z92.string().max(DECISION_RESOLUTION_MAX).optional()
|
|
5272
5016
|
});
|
|
5273
|
-
var BulkDecisionIdsRequestSchema =
|
|
5274
|
-
projectId:
|
|
5275
|
-
decisionIds:
|
|
5017
|
+
var BulkDecisionIdsRequestSchema = z92.object({
|
|
5018
|
+
projectId: z92.string().cuid("Invalid project ID"),
|
|
5019
|
+
decisionIds: z92.array(z92.string().cuid()).min(1).max(200)
|
|
5276
5020
|
});
|
|
5277
|
-
var ReopenDecisionRequestSchema =
|
|
5021
|
+
var ReopenDecisionRequestSchema = z92.object({
|
|
5278
5022
|
decisionId: decisionIdSchema,
|
|
5279
|
-
deadlineHours:
|
|
5023
|
+
deadlineHours: z92.number().min(DECISION_DEADLINE_MIN_HOURS).max(DECISION_DEADLINE_MAX_HOURS).optional()
|
|
5280
5024
|
});
|
|
5281
|
-
var UpdateDecisionRequestSchema =
|
|
5025
|
+
var UpdateDecisionRequestSchema = z92.object({
|
|
5282
5026
|
decisionId: decisionIdSchema,
|
|
5283
|
-
title:
|
|
5284
|
-
description:
|
|
5285
|
-
context:
|
|
5286
|
-
deadlineAt:
|
|
5027
|
+
title: z92.string().min(1).max(200).optional(),
|
|
5028
|
+
description: z92.string().max(CARD_DESCRIPTION_MAX).optional(),
|
|
5029
|
+
context: z92.string().min(1).optional(),
|
|
5030
|
+
deadlineAt: z92.string().datetime().optional(),
|
|
5287
5031
|
/** Only accepted while the decision has zero votes. */
|
|
5288
5032
|
options: optionsArraySchema.optional(),
|
|
5289
|
-
defaultOptionOrdinal:
|
|
5290
|
-
addImpactedCardRefs:
|
|
5291
|
-
removeImpactedCardIds:
|
|
5033
|
+
defaultOptionOrdinal: z92.number().int().min(1).max(DECISION_OPTIONS_MAX).optional(),
|
|
5034
|
+
addImpactedCardRefs: z92.array(z92.string().min(1)).max(50).optional(),
|
|
5035
|
+
removeImpactedCardIds: z92.array(z92.string().min(1)).max(50).optional()
|
|
5292
5036
|
});
|
|
5293
|
-
var GetDecisionRequestSchema =
|
|
5037
|
+
var GetDecisionRequestSchema = z92.object({
|
|
5294
5038
|
/** cuid or slug. A slug needs `projectId` to resolve unambiguously. */
|
|
5295
|
-
decisionId:
|
|
5296
|
-
projectId:
|
|
5039
|
+
decisionId: z92.string().min(1),
|
|
5040
|
+
projectId: z92.string().cuid().optional()
|
|
5297
5041
|
});
|
|
5298
|
-
var ListDecisionsRequestSchema =
|
|
5299
|
-
projectId:
|
|
5300
|
-
status:
|
|
5042
|
+
var ListDecisionsRequestSchema = z92.object({
|
|
5043
|
+
projectId: z92.string().cuid("Invalid project ID"),
|
|
5044
|
+
status: z92.enum(DECISION_STATUS_LABELS).optional(),
|
|
5301
5045
|
/** Tag name, not id — an agent knows the tag it is working under. */
|
|
5302
|
-
tag:
|
|
5046
|
+
tag: z92.string().min(1).optional(),
|
|
5303
5047
|
/** Decisions that this card is blocked by (cuid or slug). */
|
|
5304
|
-
cardId:
|
|
5305
|
-
query:
|
|
5306
|
-
limit:
|
|
5307
|
-
});
|
|
5308
|
-
var optionInput =
|
|
5309
|
-
label:
|
|
5310
|
-
description:
|
|
5311
|
-
kind:
|
|
5312
|
-
});
|
|
5313
|
-
var optionsInput =
|
|
5314
|
-
var deadlineHours =
|
|
5315
|
-
var CreateProjectDecisionRequestSchema =
|
|
5316
|
-
projectId:
|
|
5317
|
-
question:
|
|
5318
|
-
summary:
|
|
5319
|
-
context:
|
|
5048
|
+
cardId: z92.string().min(1).optional(),
|
|
5049
|
+
query: z92.string().min(1).optional(),
|
|
5050
|
+
limit: z92.number().int().min(1).max(DECISION_LIST_MAX_LIMIT).optional()
|
|
5051
|
+
});
|
|
5052
|
+
var optionInput = z10.object({
|
|
5053
|
+
label: z10.string().min(1).max(DECISION_OPTION_LABEL_MAX),
|
|
5054
|
+
description: z10.string().min(1),
|
|
5055
|
+
kind: z10.enum(DECISION_OPTION_KINDS).optional()
|
|
5056
|
+
});
|
|
5057
|
+
var optionsInput = z10.array(optionInput).min(DECISION_OPTIONS_MIN).max(DECISION_OPTIONS_MAX);
|
|
5058
|
+
var deadlineHours = z10.number().min(DECISION_DEADLINE_MIN_HOURS).max(DECISION_DEADLINE_MAX_HOURS).optional();
|
|
5059
|
+
var CreateProjectDecisionRequestSchema = z10.object({
|
|
5060
|
+
projectId: z10.string(),
|
|
5061
|
+
question: z10.string().min(1).max(200),
|
|
5062
|
+
summary: z10.string().max(CARD_DESCRIPTION_MAX).optional(),
|
|
5063
|
+
context: z10.string().min(1),
|
|
5320
5064
|
options: optionsInput,
|
|
5321
|
-
defaultOption:
|
|
5065
|
+
defaultOption: z10.number().int().min(1).max(DECISION_OPTIONS_MAX),
|
|
5322
5066
|
deadlineHours,
|
|
5323
|
-
impactedCards:
|
|
5067
|
+
impactedCards: z10.array(z10.string().min(1)).max(50).optional(),
|
|
5324
5068
|
/**
|
|
5325
5069
|
* Pod surface only: also block the card this session is working on. The
|
|
5326
5070
|
* session's task id is resolved SERVER-side from `sessionId` — a
|
|
5327
5071
|
* client-supplied card id would let a pod block any card in the project.
|
|
5328
5072
|
*/
|
|
5329
|
-
includeCurrentCard:
|
|
5330
|
-
sessionId:
|
|
5331
|
-
tagNames:
|
|
5332
|
-
raisedByPr:
|
|
5073
|
+
includeCurrentCard: z10.boolean().optional(),
|
|
5074
|
+
sessionId: z10.string().optional(),
|
|
5075
|
+
tagNames: z10.array(z10.string().min(1)).max(10).optional(),
|
|
5076
|
+
raisedByPr: z10.number().int().positive().optional(),
|
|
5333
5077
|
/** Slug or id of the card that raised the question. A link back, not a block. */
|
|
5334
|
-
raisedByCard:
|
|
5078
|
+
raisedByCard: z10.string().min(1).optional(),
|
|
5335
5079
|
/** Absolute deadline (ISO 8601). Wins over `deadlineHours` when both are sent. */
|
|
5336
|
-
deadlineAt:
|
|
5337
|
-
requestingUserId:
|
|
5338
|
-
});
|
|
5339
|
-
var ListProjectDecisionsRequestSchema =
|
|
5340
|
-
projectId:
|
|
5341
|
-
status:
|
|
5342
|
-
tag:
|
|
5343
|
-
card:
|
|
5344
|
-
query:
|
|
5345
|
-
limit:
|
|
5346
|
-
requestingUserId:
|
|
5347
|
-
});
|
|
5348
|
-
var GetProjectDecisionRequestSchema =
|
|
5349
|
-
projectId:
|
|
5350
|
-
decision:
|
|
5351
|
-
requestingUserId:
|
|
5352
|
-
});
|
|
5353
|
-
var VoteProjectDecisionRequestSchema =
|
|
5354
|
-
projectId:
|
|
5355
|
-
decision:
|
|
5080
|
+
deadlineAt: z10.string().datetime({ offset: true }).optional(),
|
|
5081
|
+
requestingUserId: z10.string().optional()
|
|
5082
|
+
});
|
|
5083
|
+
var ListProjectDecisionsRequestSchema = z10.object({
|
|
5084
|
+
projectId: z10.string(),
|
|
5085
|
+
status: z10.enum(DECISION_STATUS_LABELS).optional(),
|
|
5086
|
+
tag: z10.string().min(1).optional(),
|
|
5087
|
+
card: z10.string().min(1).optional(),
|
|
5088
|
+
query: z10.string().min(1).optional(),
|
|
5089
|
+
limit: z10.number().int().min(1).max(DECISION_LIST_MAX_LIMIT).optional(),
|
|
5090
|
+
requestingUserId: z10.string().optional()
|
|
5091
|
+
});
|
|
5092
|
+
var GetProjectDecisionRequestSchema = z10.object({
|
|
5093
|
+
projectId: z10.string(),
|
|
5094
|
+
decision: z10.string().min(1),
|
|
5095
|
+
requestingUserId: z10.string().optional()
|
|
5096
|
+
});
|
|
5097
|
+
var VoteProjectDecisionRequestSchema = z10.object({
|
|
5098
|
+
projectId: z10.string(),
|
|
5099
|
+
decision: z10.string().min(1),
|
|
5356
5100
|
/** An option id, or its 1-based position. */
|
|
5357
|
-
option:
|
|
5358
|
-
reason:
|
|
5359
|
-
requestingUserId:
|
|
5360
|
-
});
|
|
5361
|
-
var ResolveProjectDecisionRequestSchema =
|
|
5362
|
-
projectId:
|
|
5363
|
-
decision:
|
|
5364
|
-
option:
|
|
5365
|
-
resolution:
|
|
5366
|
-
notes:
|
|
5367
|
-
requestingUserId:
|
|
5368
|
-
});
|
|
5369
|
-
var UpdateProjectDecisionRequestSchema =
|
|
5370
|
-
projectId:
|
|
5371
|
-
decision:
|
|
5372
|
-
question:
|
|
5373
|
-
summary:
|
|
5374
|
-
context:
|
|
5101
|
+
option: z10.string().min(1),
|
|
5102
|
+
reason: z10.string().max(CARD_DESCRIPTION_MAX).optional(),
|
|
5103
|
+
requestingUserId: z10.string().optional()
|
|
5104
|
+
});
|
|
5105
|
+
var ResolveProjectDecisionRequestSchema = z10.object({
|
|
5106
|
+
projectId: z10.string(),
|
|
5107
|
+
decision: z10.string().min(1),
|
|
5108
|
+
option: z10.string().min(1),
|
|
5109
|
+
resolution: z10.string().min(1).max(DECISION_RESOLUTION_MAX),
|
|
5110
|
+
notes: z10.string().optional(),
|
|
5111
|
+
requestingUserId: z10.string().optional()
|
|
5112
|
+
});
|
|
5113
|
+
var UpdateProjectDecisionRequestSchema = z10.object({
|
|
5114
|
+
projectId: z10.string(),
|
|
5115
|
+
decision: z10.string().min(1),
|
|
5116
|
+
question: z10.string().min(1).max(200).optional(),
|
|
5117
|
+
summary: z10.string().max(CARD_DESCRIPTION_MAX).optional(),
|
|
5118
|
+
context: z10.string().min(1).optional(),
|
|
5375
5119
|
/** Accepted only while nobody has voted. */
|
|
5376
5120
|
options: optionsInput.optional(),
|
|
5377
|
-
defaultOption:
|
|
5378
|
-
tagNames:
|
|
5121
|
+
defaultOption: z10.number().int().min(1).max(DECISION_OPTIONS_MAX).optional(),
|
|
5122
|
+
tagNames: z10.array(z10.string().min(1)).max(10).optional(),
|
|
5379
5123
|
deadlineHours,
|
|
5380
5124
|
/** Absolute deadline (ISO 8601). Wins over `deadlineHours` when both are sent. */
|
|
5381
|
-
deadlineAt:
|
|
5382
|
-
addImpactedCards:
|
|
5383
|
-
removeImpactedCards:
|
|
5384
|
-
status:
|
|
5385
|
-
requestingUserId:
|
|
5125
|
+
deadlineAt: z10.string().datetime({ offset: true }).optional(),
|
|
5126
|
+
addImpactedCards: z10.array(z10.string().min(1)).max(50).optional(),
|
|
5127
|
+
removeImpactedCards: z10.array(z10.string().min(1)).max(50).optional(),
|
|
5128
|
+
status: z10.enum(["Cancelled", "Open"]).optional(),
|
|
5129
|
+
requestingUserId: z10.string().optional()
|
|
5130
|
+
});
|
|
5131
|
+
var EVALUATE_MAX_QUESTIONS = 32;
|
|
5132
|
+
var EVALUATE_MAX_ITEMS = 100;
|
|
5133
|
+
var EVALUATE_ITEM_MAX_CHARS = 8e3;
|
|
5134
|
+
var EVALUATE_REQUEST_MAX_CHARS = 4e5;
|
|
5135
|
+
var TEXT_MAX = 1e3;
|
|
5136
|
+
var QUESTION_KEY = /^[A-Za-z0-9_-]{1,64}$/;
|
|
5137
|
+
var EvaluateQuestionSchema = z11.object({
|
|
5138
|
+
key: z11.string().regex(QUESTION_KEY, "key must be 1-64 letters, digits, _ or -"),
|
|
5139
|
+
type: z11.enum(["noul", "choice", "score"]),
|
|
5140
|
+
instructions: z11.string().min(1).max(4e3),
|
|
5141
|
+
options: z11.array(
|
|
5142
|
+
z11.object({
|
|
5143
|
+
key: z11.string().min(1).max(200),
|
|
5144
|
+
description: z11.string().min(1).max(TEXT_MAX)
|
|
5145
|
+
})
|
|
5146
|
+
).optional(),
|
|
5147
|
+
levels: z11.array(z11.string().min(1).max(TEXT_MAX)).optional()
|
|
5148
|
+
}).superRefine((q, ctx) => {
|
|
5149
|
+
if (q.type === "choice") {
|
|
5150
|
+
const n = q.options?.length ?? 0;
|
|
5151
|
+
if (n < 2 || n > 255) {
|
|
5152
|
+
ctx.addIssue({
|
|
5153
|
+
code: z11.ZodIssueCode.custom,
|
|
5154
|
+
path: ["options"],
|
|
5155
|
+
message: "a choice question needs 2-255 options"
|
|
5156
|
+
});
|
|
5157
|
+
} else if (new Set(q.options?.map((o) => o.key)).size !== n) {
|
|
5158
|
+
ctx.addIssue({
|
|
5159
|
+
code: z11.ZodIssueCode.custom,
|
|
5160
|
+
path: ["options"],
|
|
5161
|
+
message: "option keys must be unique"
|
|
5162
|
+
});
|
|
5163
|
+
}
|
|
5164
|
+
}
|
|
5165
|
+
if (q.type === "score") {
|
|
5166
|
+
const n = q.levels?.length ?? 0;
|
|
5167
|
+
if (n < 2 || n > 10) {
|
|
5168
|
+
ctx.addIssue({
|
|
5169
|
+
code: z11.ZodIssueCode.custom,
|
|
5170
|
+
path: ["levels"],
|
|
5171
|
+
message: "a score question needs 2-10 levels"
|
|
5172
|
+
});
|
|
5173
|
+
}
|
|
5174
|
+
}
|
|
5175
|
+
});
|
|
5176
|
+
var EvaluateQuestionsRequestSchema = z11.object({
|
|
5177
|
+
projectId: z11.string(),
|
|
5178
|
+
state: z11.string().max(JEV_STATE_MAX_CHARS).optional(),
|
|
5179
|
+
items: z11.array(z11.string().max(EVALUATE_ITEM_MAX_CHARS)).max(EVALUATE_MAX_ITEMS).optional(),
|
|
5180
|
+
questions: z11.array(EvaluateQuestionSchema).min(1).max(EVALUATE_MAX_QUESTIONS)
|
|
5181
|
+
}).superRefine((req, ctx) => {
|
|
5182
|
+
if (req.state === void 0 && (!req.items || req.items.length === 0)) {
|
|
5183
|
+
ctx.addIssue({
|
|
5184
|
+
code: z11.ZodIssueCode.custom,
|
|
5185
|
+
path: ["state"],
|
|
5186
|
+
message: "pass state, or a non-empty items array"
|
|
5187
|
+
});
|
|
5188
|
+
}
|
|
5189
|
+
if (JSON.stringify(req).length > EVALUATE_REQUEST_MAX_CHARS) {
|
|
5190
|
+
ctx.addIssue({
|
|
5191
|
+
code: z11.ZodIssueCode.custom,
|
|
5192
|
+
path: [],
|
|
5193
|
+
message: `the request is over ${EVALUATE_REQUEST_MAX_CHARS} characters; split the items across calls`
|
|
5194
|
+
});
|
|
5195
|
+
}
|
|
5196
|
+
if (new Set(req.questions.map((q) => q.key)).size !== req.questions.length) {
|
|
5197
|
+
ctx.addIssue({
|
|
5198
|
+
code: z11.ZodIssueCode.custom,
|
|
5199
|
+
path: ["questions"],
|
|
5200
|
+
message: "question keys must be unique"
|
|
5201
|
+
});
|
|
5202
|
+
}
|
|
5386
5203
|
});
|
|
5204
|
+
var round3 = (n) => Math.round(n * 1e3) / 1e3;
|
|
5205
|
+
function compactAnswer(answer) {
|
|
5206
|
+
switch (answer.type) {
|
|
5207
|
+
case "noul":
|
|
5208
|
+
return round3(answer.noul);
|
|
5209
|
+
case "choice":
|
|
5210
|
+
return {
|
|
5211
|
+
choice: answer.choice,
|
|
5212
|
+
p: round3(answer.probabilities[answer.choice] ?? 0),
|
|
5213
|
+
confidence: round3(answer.confidence)
|
|
5214
|
+
};
|
|
5215
|
+
case "score":
|
|
5216
|
+
return { score: answer.score, confidence: round3(answer.confidence) };
|
|
5217
|
+
}
|
|
5218
|
+
}
|
|
5219
|
+
function formatEvaluateQuestionsResult(res) {
|
|
5220
|
+
const results = res.results.map(
|
|
5221
|
+
(r) => "error" in r ? { index: r.index, error: r.error } : {
|
|
5222
|
+
index: r.index,
|
|
5223
|
+
answers: Object.fromEntries(
|
|
5224
|
+
Object.entries(r.answers).map(([key, a]) => [key, compactAnswer(a)])
|
|
5225
|
+
)
|
|
5226
|
+
}
|
|
5227
|
+
);
|
|
5228
|
+
return JSON.stringify({ model: res.model, results });
|
|
5229
|
+
}
|
|
5387
5230
|
var MEETING_CHECKLIST_TITLE_MAX = 300;
|
|
5388
5231
|
var MEETING_TRANSCRIPT_MAX_CHARS = 2e6;
|
|
5389
5232
|
var MEETING_TITLE_MAX = 200;
|
|
@@ -5520,9 +5363,6 @@ var UpdateProjectMeetingRequestSchema = z12.object({
|
|
|
5520
5363
|
var TASK_CHAT_HISTORY_LIMIT = 20;
|
|
5521
5364
|
var PM_CHAT_HISTORY_LIMIT = 40;
|
|
5522
5365
|
var AGENT_CHAT_HISTORY_FETCH_LIMIT = Math.max(TASK_CHAT_HISTORY_LIMIT, PM_CHAT_HISTORY_LIMIT) + 10;
|
|
5523
|
-
function formatModelId(provider, model) {
|
|
5524
|
-
return `${provider}/${model}`;
|
|
5525
|
-
}
|
|
5526
5366
|
function anthropicEntry(model, label, inputPerMillion, outputPerMillion, opts = {}) {
|
|
5527
5367
|
return {
|
|
5528
5368
|
provider: "anthropic",
|
|
@@ -5861,45 +5701,391 @@ var BOARD_CARD_TYPES = surfaceTypes("board");
|
|
|
5861
5701
|
var REPORT_CARD_TYPES = surfaceTypes("report");
|
|
5862
5702
|
var DECISION_CARD_TYPES = surfaceTypes("decision");
|
|
5863
5703
|
|
|
5864
|
-
// src/tools/
|
|
5865
|
-
function
|
|
5866
|
-
|
|
5867
|
-
|
|
5868
|
-
|
|
5869
|
-
|
|
5870
|
-
|
|
5871
|
-
|
|
5872
|
-
|
|
5873
|
-
|
|
5704
|
+
// src/tools/checklists.ts
|
|
5705
|
+
function renderManualTestGroups(groups) {
|
|
5706
|
+
const lines = [];
|
|
5707
|
+
for (const g of groups) {
|
|
5708
|
+
lines.push(`## ${g.title} [${g.status}] (${g.slug})`);
|
|
5709
|
+
for (const t of g.tests) {
|
|
5710
|
+
const mark = t.status === "approved" ? "\u2713" : t.status === "rejected" ? "\u2717" : "\u25CB";
|
|
5711
|
+
lines.push(` ${mark} ${t.title} \u2014 ${t.status}`);
|
|
5712
|
+
if (t.status === "rejected") {
|
|
5713
|
+
for (const f2 of t.failures ?? []) {
|
|
5714
|
+
lines.push(` \u26A0 ${f2.userName ?? "Someone"}: ${f2.reason ?? "(no message)"}`);
|
|
5715
|
+
}
|
|
5716
|
+
}
|
|
5717
|
+
}
|
|
5718
|
+
lines.push("");
|
|
5719
|
+
}
|
|
5720
|
+
return lines.join("\n").trimEnd();
|
|
5874
5721
|
}
|
|
5875
|
-
|
|
5876
|
-
|
|
5877
|
-
|
|
5878
|
-
|
|
5879
|
-
|
|
5880
|
-
return { content: [{ type: "text", text: JSON.stringify(result, null, 2) }] };
|
|
5881
|
-
});
|
|
5882
|
-
registerContractTool(server2, listProjectChannelsContract, async (params) => {
|
|
5883
|
-
const channels = await conn2.listProjectChannels(params.projectId);
|
|
5884
|
-
if (channels.length === 0) {
|
|
5885
|
-
return {
|
|
5886
|
-
content: [
|
|
5887
|
-
{
|
|
5888
|
-
type: "text",
|
|
5889
|
-
text: "No work channels are registered for this project. An admin registers them in project settings; until then no channel is reachable, whatever the bot can see in the workspace."
|
|
5890
|
-
}
|
|
5891
|
-
]
|
|
5892
|
-
};
|
|
5722
|
+
function registerListManualTests(server2, conn2) {
|
|
5723
|
+
registerContractTool(server2, listManualTestsContract, async (params) => {
|
|
5724
|
+
const items = await conn2.listManualTests(params.taskId, params.projectId);
|
|
5725
|
+
if (items.length === 0) {
|
|
5726
|
+
return { content: [{ type: "text", text: "No manual tests recorded for this task." }] };
|
|
5893
5727
|
}
|
|
5894
|
-
|
|
5728
|
+
const lines = items.flatMap((item, i) => {
|
|
5729
|
+
const checked = item.checked ? "[x]" : "[ ]";
|
|
5730
|
+
const row = [`${i + 1}. ${checked} ${item.title}`];
|
|
5731
|
+
for (const f2 of item.failures ?? []) {
|
|
5732
|
+
const who = f2.userName ?? "Someone";
|
|
5733
|
+
const reason = f2.reason ?? "(no message)";
|
|
5734
|
+
row.push(` \u26A0 Failed (${who}): ${reason}`);
|
|
5735
|
+
}
|
|
5736
|
+
return row;
|
|
5737
|
+
});
|
|
5738
|
+
return { content: [{ type: "text", text: lines.join("\n") }] };
|
|
5895
5739
|
});
|
|
5896
|
-
|
|
5897
|
-
|
|
5898
|
-
|
|
5740
|
+
}
|
|
5741
|
+
function registerSetManualTests(server2, conn2) {
|
|
5742
|
+
registerContractTool(server2, setManualTestsContract, async (params) => {
|
|
5743
|
+
const result = await conn2.setManualTests(params.taskId, params.items, params.projectId);
|
|
5744
|
+
const parts = [`Created ${result.created} manual test item(s).`];
|
|
5745
|
+
if (result.skipped > 0) parts.push(`Skipped ${result.skipped} duplicate(s).`);
|
|
5746
|
+
for (const item of result.rejected ?? []) parts.push(describeManualTestRejection(item));
|
|
5747
|
+
return { content: [{ type: "text", text: parts.join(" ") }] };
|
|
5899
5748
|
});
|
|
5900
|
-
|
|
5901
|
-
|
|
5902
|
-
|
|
5749
|
+
}
|
|
5750
|
+
function registerEditManualTest(server2, conn2) {
|
|
5751
|
+
registerContractTool(server2, editManualTestContract, async (params) => {
|
|
5752
|
+
await conn2.editManualTest(params.taskId, params.title, params.newTitle, params.projectId);
|
|
5753
|
+
return { content: [{ type: "text", text: `Updated manual test to "${params.newTitle}".` }] };
|
|
5754
|
+
});
|
|
5755
|
+
}
|
|
5756
|
+
function registerRemoveManualTest(server2, conn2) {
|
|
5757
|
+
registerContractTool(server2, removeManualTestContract, async (params) => {
|
|
5758
|
+
await conn2.removeManualTest(params.taskId, params.title, params.projectId);
|
|
5759
|
+
return { content: [{ type: "text", text: `Removed manual test "${params.title}".` }] };
|
|
5760
|
+
});
|
|
5761
|
+
}
|
|
5762
|
+
function registerApproveManualTest(server2, conn2) {
|
|
5763
|
+
registerContractTool(server2, approveManualTestContract, async (params) => {
|
|
5764
|
+
await conn2.approveManualTest(params.taskId, params.title, params.projectId);
|
|
5765
|
+
return { content: [{ type: "text", text: `Approved manual test "${params.title}".` }] };
|
|
5766
|
+
});
|
|
5767
|
+
}
|
|
5768
|
+
function registerRejectManualTest(server2, conn2) {
|
|
5769
|
+
registerContractTool(server2, rejectManualTestContract, async (params) => {
|
|
5770
|
+
await conn2.rejectManualTest(params.taskId, params.title, params.reason, params.projectId);
|
|
5771
|
+
return {
|
|
5772
|
+
content: [
|
|
5773
|
+
{
|
|
5774
|
+
type: "text",
|
|
5775
|
+
text: `Flagged an issue with manual test "${params.title}": ${params.reason}`
|
|
5776
|
+
}
|
|
5777
|
+
]
|
|
5778
|
+
};
|
|
5779
|
+
});
|
|
5780
|
+
}
|
|
5781
|
+
function registerQueryManualTests(server2, conn2) {
|
|
5782
|
+
registerContractTool(server2, queryManualTestsContract, async (params) => {
|
|
5783
|
+
const groups = await conn2.queryManualTests({
|
|
5784
|
+
projectId: params.projectId,
|
|
5785
|
+
cardStatuses: params.cardStatuses,
|
|
5786
|
+
testStatuses: params.testStatuses
|
|
5787
|
+
});
|
|
5788
|
+
if (groups.length === 0) {
|
|
5789
|
+
return { content: [{ type: "text", text: "No manual tests match those filters." }] };
|
|
5790
|
+
}
|
|
5791
|
+
return { content: [{ type: "text", text: renderManualTestGroups(groups) }] };
|
|
5792
|
+
});
|
|
5793
|
+
}
|
|
5794
|
+
function registerChecklistTools(server2, conn2) {
|
|
5795
|
+
registerListManualTests(server2, conn2);
|
|
5796
|
+
registerQueryManualTests(server2, conn2);
|
|
5797
|
+
registerSetManualTests(server2, conn2);
|
|
5798
|
+
registerEditManualTest(server2, conn2);
|
|
5799
|
+
registerRemoveManualTest(server2, conn2);
|
|
5800
|
+
registerApproveManualTest(server2, conn2);
|
|
5801
|
+
registerRejectManualTest(server2, conn2);
|
|
5802
|
+
}
|
|
5803
|
+
|
|
5804
|
+
// src/tools/workspace.ts
|
|
5805
|
+
import { z as z13 } from "zod";
|
|
5806
|
+
|
|
5807
|
+
// src/workspace-ssh-tunnel.ts
|
|
5808
|
+
import net from "net";
|
|
5809
|
+
import { once } from "events";
|
|
5810
|
+
import { WebSocket } from "ws";
|
|
5811
|
+
var WORKSPACE_TUNNEL_HEARTBEAT_MS = 3e4;
|
|
5812
|
+
function withPort(url, port) {
|
|
5813
|
+
const parsed = new URL(url);
|
|
5814
|
+
parsed.searchParams.set("port", String(port));
|
|
5815
|
+
return parsed.toString();
|
|
5816
|
+
}
|
|
5817
|
+
function toBuffer(data) {
|
|
5818
|
+
if (Buffer.isBuffer(data)) return data;
|
|
5819
|
+
if (data instanceof ArrayBuffer) return Buffer.from(data);
|
|
5820
|
+
return Buffer.concat(data);
|
|
5821
|
+
}
|
|
5822
|
+
function startHeartbeat(ws) {
|
|
5823
|
+
const timer = setInterval(() => {
|
|
5824
|
+
if (ws.readyState === WebSocket.OPEN) ws.ping();
|
|
5825
|
+
}, WORKSPACE_TUNNEL_HEARTBEAT_MS);
|
|
5826
|
+
timer.unref?.();
|
|
5827
|
+
return () => clearInterval(timer);
|
|
5828
|
+
}
|
|
5829
|
+
function bridgeClient(client, options) {
|
|
5830
|
+
let ws = null;
|
|
5831
|
+
let ready = false;
|
|
5832
|
+
const pending = [];
|
|
5833
|
+
client.on("data", (data) => {
|
|
5834
|
+
const chunk = Buffer.isBuffer(data) ? data : Buffer.from(data);
|
|
5835
|
+
if (ready && ws && ws.readyState === WebSocket.OPEN) ws.send(chunk);
|
|
5836
|
+
else pending.push(chunk);
|
|
5837
|
+
});
|
|
5838
|
+
client.on("close", () => ws?.close());
|
|
5839
|
+
client.on("error", () => ws?.close());
|
|
5840
|
+
options.resolveTarget().then(({ tunnelUrl, attachToken }) => {
|
|
5841
|
+
const socket = new WebSocket(withPort(tunnelUrl, options.remotePort), {
|
|
5842
|
+
headers: { "x-workspace-attach-token": attachToken }
|
|
5843
|
+
});
|
|
5844
|
+
ws = socket;
|
|
5845
|
+
const stopHeartbeat = startHeartbeat(socket);
|
|
5846
|
+
socket.on("message", (data) => {
|
|
5847
|
+
const chunk = toBuffer(data);
|
|
5848
|
+
if (!ready) {
|
|
5849
|
+
const text = chunk.toString("utf8");
|
|
5850
|
+
if (text === JSON.stringify({ ok: true })) {
|
|
5851
|
+
ready = true;
|
|
5852
|
+
for (const buffered of pending.splice(0)) socket.send(buffered);
|
|
5853
|
+
return;
|
|
5854
|
+
}
|
|
5855
|
+
client.destroy(new Error(`Workspace tunnel rejected connection: ${text}`));
|
|
5856
|
+
socket.close();
|
|
5857
|
+
return;
|
|
5858
|
+
}
|
|
5859
|
+
client.write(chunk);
|
|
5860
|
+
});
|
|
5861
|
+
socket.on("close", () => {
|
|
5862
|
+
stopHeartbeat();
|
|
5863
|
+
client.destroy();
|
|
5864
|
+
});
|
|
5865
|
+
socket.on("error", (err) => {
|
|
5866
|
+
stopHeartbeat();
|
|
5867
|
+
client.destroy(err);
|
|
5868
|
+
});
|
|
5869
|
+
}).catch((err) => {
|
|
5870
|
+
client.destroy(err instanceof Error ? err : new Error(String(err)));
|
|
5871
|
+
});
|
|
5872
|
+
}
|
|
5873
|
+
async function startWorkspaceSshTunnel(options) {
|
|
5874
|
+
const host = options.host ?? "127.0.0.1";
|
|
5875
|
+
const server2 = net.createServer((client) => bridgeClient(client, options));
|
|
5876
|
+
const ac = new AbortController();
|
|
5877
|
+
server2.listen(options.preferredLocalPort ?? 0, host);
|
|
5878
|
+
try {
|
|
5879
|
+
await Promise.race([
|
|
5880
|
+
once(server2, "listening", { signal: ac.signal }),
|
|
5881
|
+
once(server2, "error", { signal: ac.signal }).then(([err]) => {
|
|
5882
|
+
throw err instanceof Error ? err : new Error(String(err));
|
|
5883
|
+
})
|
|
5884
|
+
]);
|
|
5885
|
+
} catch (err) {
|
|
5886
|
+
server2.close();
|
|
5887
|
+
throw new Error(
|
|
5888
|
+
`Failed to bind workspace SSH tunnel: ${err instanceof Error ? err.message : String(err)}`,
|
|
5889
|
+
{ cause: err }
|
|
5890
|
+
);
|
|
5891
|
+
} finally {
|
|
5892
|
+
ac.abort();
|
|
5893
|
+
}
|
|
5894
|
+
const address = server2.address();
|
|
5895
|
+
if (!address || typeof address === "string") {
|
|
5896
|
+
server2.close();
|
|
5897
|
+
throw new Error("Failed to bind workspace SSH tunnel");
|
|
5898
|
+
}
|
|
5899
|
+
return {
|
|
5900
|
+
host,
|
|
5901
|
+
port: address.port,
|
|
5902
|
+
close: () => new Promise((resolve, reject) => {
|
|
5903
|
+
server2.close((err) => {
|
|
5904
|
+
if (err) reject(err);
|
|
5905
|
+
else resolve();
|
|
5906
|
+
});
|
|
5907
|
+
})
|
|
5908
|
+
};
|
|
5909
|
+
}
|
|
5910
|
+
|
|
5911
|
+
// src/tools/workspace.ts
|
|
5912
|
+
var activeTunnels = /* @__PURE__ */ new Map();
|
|
5913
|
+
function createTunnelId(taskId, port) {
|
|
5914
|
+
return `${taskId}:${port}:${Date.now().toString(36)}:${Math.random().toString(36).slice(2, 8)}`;
|
|
5915
|
+
}
|
|
5916
|
+
function localUrlFor(port, host, localPort) {
|
|
5917
|
+
if (port === 2222) return void 0;
|
|
5918
|
+
return `http://${host}:${localPort}`;
|
|
5919
|
+
}
|
|
5920
|
+
function registerAttachInfoTool(server2, conn2) {
|
|
5921
|
+
server2.tool(
|
|
5922
|
+
"workspace_attach_info",
|
|
5923
|
+
"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.",
|
|
5924
|
+
{
|
|
5925
|
+
taskId: z13.string().describe("The task ID"),
|
|
5926
|
+
sshPublicKey: z13.string().optional().describe("Optional OpenSSH public key to install into the workspace")
|
|
5927
|
+
},
|
|
5928
|
+
async ({ taskId, sshPublicKey }) => {
|
|
5929
|
+
const info = await conn2.getWorkspaceAttachInfo(taskId, sshPublicKey);
|
|
5930
|
+
return { content: [{ type: "text", text: JSON.stringify(info, null, 2) }] };
|
|
5931
|
+
}
|
|
5932
|
+
);
|
|
5933
|
+
}
|
|
5934
|
+
function registerPreviewUrlsTool(server2, conn2) {
|
|
5935
|
+
server2.tool(
|
|
5936
|
+
"workspace_preview_urls",
|
|
5937
|
+
"Return the hosted preview URLs and preview ports for a running task Claudespace. This mirrors the web UI preview link metadata.",
|
|
5938
|
+
{
|
|
5939
|
+
taskId: z13.string().describe("The task ID")
|
|
5940
|
+
},
|
|
5941
|
+
async ({ taskId }) => {
|
|
5942
|
+
const info = await conn2.getWorkspaceAttachInfo(taskId);
|
|
5943
|
+
return {
|
|
5944
|
+
content: [
|
|
5945
|
+
{
|
|
5946
|
+
type: "text",
|
|
5947
|
+
text: JSON.stringify(
|
|
5948
|
+
{
|
|
5949
|
+
taskId: info.taskId,
|
|
5950
|
+
sessionId: info.sessionId,
|
|
5951
|
+
previewPorts: info.previewPorts,
|
|
5952
|
+
previewUrls: info.previewUrls,
|
|
5953
|
+
previewLinks: info.previewLinks ?? []
|
|
5954
|
+
},
|
|
5955
|
+
null,
|
|
5956
|
+
2
|
|
5957
|
+
)
|
|
5958
|
+
}
|
|
5959
|
+
]
|
|
5960
|
+
};
|
|
5961
|
+
}
|
|
5962
|
+
);
|
|
5963
|
+
}
|
|
5964
|
+
function registerStartTunnelTool(server2, conn2, startTunnel) {
|
|
5965
|
+
server2.tool(
|
|
5966
|
+
"workspace_start_tunnel",
|
|
5967
|
+
"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.",
|
|
5968
|
+
{
|
|
5969
|
+
taskId: z13.string().describe("The task ID"),
|
|
5970
|
+
port: z13.number().optional().describe("Remote Claudespace port. Defaults to SSH port 2222."),
|
|
5971
|
+
preferredLocalPort: z13.number().optional().describe("Preferred local loopback port. If omitted, the OS chooses one."),
|
|
5972
|
+
sshPublicKey: z13.string().optional().describe("Optional OpenSSH public key to install before opening the tunnel")
|
|
5973
|
+
},
|
|
5974
|
+
async ({ taskId, port, preferredLocalPort, sshPublicKey }) => {
|
|
5975
|
+
const info = await conn2.getWorkspaceAttachInfo(taskId, sshPublicKey);
|
|
5976
|
+
if (!info.tunnelUrl || !info.attachToken) {
|
|
5977
|
+
throw new Error(
|
|
5978
|
+
"Workspace tunnels are unavailable for this task (GitHub Codespace-backed environments expose previews only \u2014 use workspace_preview_urls)."
|
|
5979
|
+
);
|
|
5980
|
+
}
|
|
5981
|
+
const remotePort = port ?? info.ssh?.remotePort ?? 2222;
|
|
5982
|
+
const handle = await startTunnel({
|
|
5983
|
+
remotePort,
|
|
5984
|
+
preferredLocalPort,
|
|
5985
|
+
// Re-mint per connection so the tunnel outlives the attach-token TTL.
|
|
5986
|
+
// The key (if any) is already installed by the call above; the refetch
|
|
5987
|
+
// only needs a fresh token, so it omits sshPublicKey.
|
|
5988
|
+
resolveTarget: async () => {
|
|
5989
|
+
const fresh = await conn2.getWorkspaceAttachInfo(taskId);
|
|
5990
|
+
if (!fresh.tunnelUrl || !fresh.attachToken)
|
|
5991
|
+
throw new Error("Workspace tunnel target is no longer available");
|
|
5992
|
+
return { tunnelUrl: fresh.tunnelUrl, attachToken: fresh.attachToken };
|
|
5993
|
+
}
|
|
5994
|
+
});
|
|
5995
|
+
const tunnelId = createTunnelId(taskId, remotePort);
|
|
5996
|
+
activeTunnels.set(tunnelId, handle);
|
|
5997
|
+
return {
|
|
5998
|
+
content: [
|
|
5999
|
+
{
|
|
6000
|
+
type: "text",
|
|
6001
|
+
text: JSON.stringify(
|
|
6002
|
+
{
|
|
6003
|
+
tunnelId,
|
|
6004
|
+
taskId,
|
|
6005
|
+
remotePort,
|
|
6006
|
+
localHost: handle.host,
|
|
6007
|
+
localPort: handle.port,
|
|
6008
|
+
localUrl: localUrlFor(remotePort, handle.host, handle.port),
|
|
6009
|
+
// `-o ServerAliveInterval=30` drives client-side keepalives so an
|
|
6010
|
+
// idle session keeps the tunnel warm (the tunnel layer pings too,
|
|
6011
|
+
// but the client must keep its own TCP path alive across every hop).
|
|
6012
|
+
sshCommand: remotePort === 2222 ? `ssh -o ServerAliveInterval=30 -o ServerAliveCountMax=3 -p ${handle.port} ${info.ssh?.username ?? "conveyor"}@${handle.host}` : void 0,
|
|
6013
|
+
sftpCommand: remotePort === 2222 ? `sftp -o ServerAliveInterval=30 -o ServerAliveCountMax=3 -P ${handle.port} ${info.ssh?.username ?? "conveyor"}@${handle.host}` : void 0,
|
|
6014
|
+
previewPorts: info.previewPorts,
|
|
6015
|
+
previewUrls: info.previewUrls,
|
|
6016
|
+
expiresAt: info.expiresAt
|
|
6017
|
+
},
|
|
6018
|
+
null,
|
|
6019
|
+
2
|
|
6020
|
+
)
|
|
6021
|
+
}
|
|
6022
|
+
]
|
|
6023
|
+
};
|
|
6024
|
+
}
|
|
6025
|
+
);
|
|
6026
|
+
}
|
|
6027
|
+
function registerStopTunnelTool(server2) {
|
|
6028
|
+
server2.tool(
|
|
6029
|
+
"workspace_stop_tunnel",
|
|
6030
|
+
"Stop a local workspace tunnel previously opened by workspace_start_tunnel.",
|
|
6031
|
+
{
|
|
6032
|
+
tunnelId: z13.string().describe("Tunnel id returned by workspace_start_tunnel")
|
|
6033
|
+
},
|
|
6034
|
+
async ({ tunnelId }) => {
|
|
6035
|
+
const tunnel = activeTunnels.get(tunnelId);
|
|
6036
|
+
if (!tunnel) throw new Error("Workspace tunnel not found");
|
|
6037
|
+
await tunnel.close();
|
|
6038
|
+
activeTunnels.delete(tunnelId);
|
|
6039
|
+
return { content: [{ type: "text", text: "Workspace tunnel stopped" }] };
|
|
6040
|
+
}
|
|
6041
|
+
);
|
|
6042
|
+
}
|
|
6043
|
+
function registerWorkspaceTools(server2, conn2, deps = {}) {
|
|
6044
|
+
registerAttachInfoTool(server2, conn2);
|
|
6045
|
+
registerPreviewUrlsTool(server2, conn2);
|
|
6046
|
+
registerStartTunnelTool(server2, conn2, deps.startTunnel ?? startWorkspaceSshTunnel);
|
|
6047
|
+
registerStopTunnelTool(server2);
|
|
6048
|
+
}
|
|
6049
|
+
|
|
6050
|
+
// src/tools/logs.ts
|
|
6051
|
+
function registerLogTools(server2, conn2) {
|
|
6052
|
+
registerContractTool(server2, queryGcpLogsContract, async (params) => {
|
|
6053
|
+
const text = await runQueryGcpLogs(conn2, params);
|
|
6054
|
+
return { content: [{ type: "text", text }] };
|
|
6055
|
+
});
|
|
6056
|
+
registerContractTool(server2, queryGrafanaLogsContract, async (params) => {
|
|
6057
|
+
const text = await runQueryGrafanaLogs(conn2, params);
|
|
6058
|
+
return { content: [{ type: "text", text }] };
|
|
6059
|
+
});
|
|
6060
|
+
}
|
|
6061
|
+
|
|
6062
|
+
// src/tools/integrations.ts
|
|
6063
|
+
function registerIntegrationTools(server2, conn2) {
|
|
6064
|
+
registerContractTool(server2, listProjectIntegrationsContract, async (params) => {
|
|
6065
|
+
const result = await conn2.listProjectIntegrations(params.projectId);
|
|
6066
|
+
return { content: [{ type: "text", text: JSON.stringify(result, null, 2) }] };
|
|
6067
|
+
});
|
|
6068
|
+
registerContractTool(server2, listProjectChannelsContract, async (params) => {
|
|
6069
|
+
const channels = await conn2.listProjectChannels(params.projectId);
|
|
6070
|
+
if (channels.length === 0) {
|
|
6071
|
+
return {
|
|
6072
|
+
content: [
|
|
6073
|
+
{
|
|
6074
|
+
type: "text",
|
|
6075
|
+
text: "No work channels are registered for this project. An admin registers them in project settings; until then no channel is reachable, whatever the bot can see in the workspace."
|
|
6076
|
+
}
|
|
6077
|
+
]
|
|
6078
|
+
};
|
|
6079
|
+
}
|
|
6080
|
+
return { content: [{ type: "text", text: JSON.stringify(channels, null, 2) }] };
|
|
6081
|
+
});
|
|
6082
|
+
registerContractTool(server2, readChannelMessagesContract, async (params) => {
|
|
6083
|
+
const result = await conn2.readChannelMessages(params);
|
|
6084
|
+
return { content: [{ type: "text", text: JSON.stringify(result, null, 2) }] };
|
|
6085
|
+
});
|
|
6086
|
+
registerContractTool(server2, getAnalyticsSummaryContract, async (params) => {
|
|
6087
|
+
const res = await conn2.getProjectAnalyticsSummary(params);
|
|
6088
|
+
if (!res.configured) {
|
|
5903
6089
|
return {
|
|
5904
6090
|
content: [
|
|
5905
6091
|
{
|
|
@@ -6112,6 +6298,14 @@ function registerDecisionTools(server2, conn2) {
|
|
|
6112
6298
|
registerDecisionWrites(server2, conn2);
|
|
6113
6299
|
}
|
|
6114
6300
|
|
|
6301
|
+
// src/tools/evaluate.ts
|
|
6302
|
+
function registerEvaluateTools(server2, conn2) {
|
|
6303
|
+
registerContractTool(server2, evaluateQuestionsContract, async (params) => {
|
|
6304
|
+
const res = await conn2.evaluateQuestions(params);
|
|
6305
|
+
return { content: [{ type: "text", text: formatEvaluateQuestionsResult(res) }] };
|
|
6306
|
+
});
|
|
6307
|
+
}
|
|
6308
|
+
|
|
6115
6309
|
// src/tools/index.ts
|
|
6116
6310
|
function registerAllTools(server2, conn2) {
|
|
6117
6311
|
registerProjectTools(server2, conn2);
|
|
@@ -6131,6 +6325,7 @@ function registerAllTools(server2, conn2) {
|
|
|
6131
6325
|
registerDriveTools(server2, conn2);
|
|
6132
6326
|
registerMeetingTools(server2, conn2);
|
|
6133
6327
|
registerDecisionTools(server2, conn2);
|
|
6328
|
+
registerEvaluateTools(server2, conn2);
|
|
6134
6329
|
}
|
|
6135
6330
|
|
|
6136
6331
|
// src/cli.ts
|