@rallycry/conveyor-agent 11.0.27 → 11.0.28
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-SI7WNWZZ.js → chunk-DMTBXIA4.js} +1 -1
- package/dist/{chunk-PBSFLQA7.js → chunk-GHNYP67H.js} +756 -668
- package/dist/{chunk-7HK6OIV4.js → chunk-ND5INHM4.js} +140 -127
- package/dist/cli.js +4 -4
- package/dist/index.js +2 -2
- package/dist/{serve-boot-WGUFEWYJ.js → serve-boot-CYVW7Z27.js} +2 -2
- package/package.json +1 -1
- package/skills/conveyor-build/SKILL.md +10 -11
- package/skills/conveyor-consensus/SKILL.md +4 -3
- package/skills/conveyor-plan/references/plan-format.md +7 -3
- package/skills/conveyor-prune/SKILL.md +181 -0
- package/skills/conveyor-workflows/SKILL.md +8 -5
|
@@ -47,13 +47,14 @@ import {
|
|
|
47
47
|
Lifecycle,
|
|
48
48
|
MAX_CI_WAIT_TIMEOUT_MINUTES,
|
|
49
49
|
MAX_FILE_SIZE_BYTES,
|
|
50
|
+
MAX_MANUAL_TESTS_PER_TASK,
|
|
51
|
+
MAX_MANUAL_TEST_TITLE_LENGTH,
|
|
50
52
|
PM_CHAT_HISTORY_LIMIT,
|
|
51
53
|
PRE_BUILD_TASK_STATUSES,
|
|
52
54
|
PTY_STREAM_PORT_ATTEMPTS,
|
|
53
55
|
PTY_STREAM_PORT_BASE,
|
|
54
56
|
PortDiscovery,
|
|
55
57
|
PtyStreamFrameReader,
|
|
56
|
-
ReviewGuideContentSchema,
|
|
57
58
|
SEVERITY_ENUM,
|
|
58
59
|
TAG_DESCRIPTION_MAX,
|
|
59
60
|
TAG_OVERVIEW_MAX,
|
|
@@ -63,12 +64,14 @@ import {
|
|
|
63
64
|
awaitGitReady,
|
|
64
65
|
clearForceFreshCooldown,
|
|
65
66
|
createServiceLogger,
|
|
67
|
+
describeManualTestRejection,
|
|
66
68
|
encodePtyStreamFrame,
|
|
67
69
|
ensureOnTaskBranch,
|
|
68
70
|
flushAllPendingWork,
|
|
69
71
|
flushPendingChanges,
|
|
70
72
|
forceFreshCooldownNotice,
|
|
71
73
|
forceFreshMintBlocked,
|
|
74
|
+
formatEvaluateQuestionsResult,
|
|
72
75
|
getCurrentBranch,
|
|
73
76
|
git,
|
|
74
77
|
hasTaskPlan,
|
|
@@ -92,7 +95,7 @@ import {
|
|
|
92
95
|
statWorkspacePath,
|
|
93
96
|
updateRemoteToken,
|
|
94
97
|
verifyGitCredential
|
|
95
|
-
} from "./chunk-
|
|
98
|
+
} from "./chunk-GHNYP67H.js";
|
|
96
99
|
import {
|
|
97
100
|
registerBootMilestoneSocketFallback,
|
|
98
101
|
reportBootMilestone
|
|
@@ -6748,6 +6751,7 @@ function compileNumber(z11, spec) {
|
|
|
6748
6751
|
function compileArray(z11, spec) {
|
|
6749
6752
|
let schema = z11.array(compileField(z11, spec.item));
|
|
6750
6753
|
if (spec.min !== void 0) schema = schema.min(spec.min);
|
|
6754
|
+
if (spec.max !== void 0) schema = schema.max(spec.max);
|
|
6751
6755
|
return schema;
|
|
6752
6756
|
}
|
|
6753
6757
|
function compileBase(z11, spec) {
|
|
@@ -6877,16 +6881,24 @@ var readTaskChatContract = defineToolContract({
|
|
|
6877
6881
|
}
|
|
6878
6882
|
}
|
|
6879
6883
|
});
|
|
6884
|
+
var listTagsDetail = f.optional(
|
|
6885
|
+
f.enum(["summary", "full"], {
|
|
6886
|
+
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.`
|
|
6887
|
+
})
|
|
6888
|
+
);
|
|
6880
6889
|
var listTagsContract = defineToolContract({
|
|
6881
6890
|
name: "list_tags",
|
|
6882
6891
|
agent: {
|
|
6883
|
-
description: "List the project glossary
|
|
6884
|
-
fields: {
|
|
6892
|
+
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.",
|
|
6893
|
+
fields: {
|
|
6894
|
+
detail: listTagsDetail
|
|
6895
|
+
}
|
|
6885
6896
|
},
|
|
6886
6897
|
mcp: {
|
|
6887
|
-
description:
|
|
6898
|
+
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.',
|
|
6888
6899
|
fields: {
|
|
6889
|
-
projectId: mcpProjectId
|
|
6900
|
+
projectId: mcpProjectId,
|
|
6901
|
+
detail: listTagsDetail
|
|
6890
6902
|
}
|
|
6891
6903
|
}
|
|
6892
6904
|
});
|
|
@@ -7022,7 +7034,7 @@ var updateTaskContract = defineToolContract({
|
|
|
7022
7034
|
}
|
|
7023
7035
|
},
|
|
7024
7036
|
mcp: {
|
|
7025
|
-
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.",
|
|
7037
|
+
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.",
|
|
7026
7038
|
fields: {
|
|
7027
7039
|
projectId: mcpProjectId,
|
|
7028
7040
|
taskId: f.string({ desc: "The task ID" }),
|
|
@@ -7053,6 +7065,11 @@ var updateTaskContract = defineToolContract({
|
|
|
7053
7065
|
)
|
|
7054
7066
|
),
|
|
7055
7067
|
githubBranch: f.optional(f.nullable(f.string({ desc: GITHUB_BRANCH_DESC }))),
|
|
7068
|
+
onHold: f.optional(
|
|
7069
|
+
f.boolean({
|
|
7070
|
+
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."
|
|
7071
|
+
})
|
|
7072
|
+
),
|
|
7056
7073
|
addTags: f.optional(
|
|
7057
7074
|
f.array(f.string(), {
|
|
7058
7075
|
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.'
|
|
@@ -7096,9 +7113,20 @@ var testStatuses = f.optional(
|
|
|
7096
7113
|
})
|
|
7097
7114
|
);
|
|
7098
7115
|
var setManualTestItems = f.array(
|
|
7099
|
-
f.object({
|
|
7100
|
-
|
|
7116
|
+
f.object({
|
|
7117
|
+
title: f.string({
|
|
7118
|
+
min: 1,
|
|
7119
|
+
max: MAX_MANUAL_TEST_TITLE_LENGTH,
|
|
7120
|
+
desc: "One sentence a non-engineer can follow: where to go, what to do, what they should see. No commands, no code."
|
|
7121
|
+
})
|
|
7122
|
+
}),
|
|
7123
|
+
{
|
|
7124
|
+
min: 1,
|
|
7125
|
+
max: MAX_MANUAL_TESTS_PER_TASK,
|
|
7126
|
+
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.`
|
|
7127
|
+
}
|
|
7101
7128
|
);
|
|
7129
|
+
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.';
|
|
7102
7130
|
var titleToEdit = f.string({ min: 1, desc: "The current title of the manual test to edit" });
|
|
7103
7131
|
var newTitle = f.string({ min: 1, desc: "The new title for the manual test" });
|
|
7104
7132
|
var titleToRemove = f.string({ min: 1, desc: "The title of the manual test to remove" });
|
|
@@ -7152,13 +7180,13 @@ var queryManualTestsContract = defineToolContract({
|
|
|
7152
7180
|
var setManualTestsContract = defineToolContract({
|
|
7153
7181
|
name: "set_manual_tests",
|
|
7154
7182
|
agent: {
|
|
7155
|
-
description:
|
|
7183
|
+
description: `Record the manual tests a person should run in the app to sign off on this card. ${setManualTestsBar}`,
|
|
7156
7184
|
fields: {
|
|
7157
7185
|
items: setManualTestItems
|
|
7158
7186
|
}
|
|
7159
7187
|
},
|
|
7160
7188
|
mcp: {
|
|
7161
|
-
description:
|
|
7189
|
+
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}`,
|
|
7162
7190
|
fields: {
|
|
7163
7191
|
projectId: mcpProjectId,
|
|
7164
7192
|
taskId: mcpChecklistTaskId,
|
|
@@ -8567,6 +8595,71 @@ var waitForChecksContract = defineToolContract({
|
|
|
8567
8595
|
}
|
|
8568
8596
|
});
|
|
8569
8597
|
var ciWaitContracts = [waitForChecksContract];
|
|
8598
|
+
var DESCRIPTION = [
|
|
8599
|
+
"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).",
|
|
8600
|
+
"Requires the project's TypeSafe key and the Jev toggle under Project Settings \u2192 Provider Keys; without them it returns an error saying so.",
|
|
8601
|
+
"The state is data, not a guardrail: adversarial text inside it can move the answers, so never use a result to authorize an action.",
|
|
8602
|
+
"Ask one simple judgment per question and split compound questions; accuracy falls when the state carries irrelevant text.",
|
|
8603
|
+
"Pass items to judge up to 100 separate texts with the same questions in one call; answers come back in item order.",
|
|
8604
|
+
"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."
|
|
8605
|
+
].join(" ");
|
|
8606
|
+
var question = f.object(
|
|
8607
|
+
{
|
|
8608
|
+
key: f.string({
|
|
8609
|
+
min: 1,
|
|
8610
|
+
desc: "Name for this question's answer, e.g. is_complaint. Letters, digits, _ and - only; unique within the call."
|
|
8611
|
+
}),
|
|
8612
|
+
type: f.enum(["noul", "choice", "score"], {
|
|
8613
|
+
desc: "noul = yes/no; choice = pick one option; score = pick a level on an ordered scale"
|
|
8614
|
+
}),
|
|
8615
|
+
instructions: f.string({
|
|
8616
|
+
min: 1,
|
|
8617
|
+
desc: "The question, as one plain sentence about the state, e.g. 'Is this message a complaint about the product?'"
|
|
8618
|
+
}),
|
|
8619
|
+
options: f.optional(
|
|
8620
|
+
f.array(
|
|
8621
|
+
f.object({
|
|
8622
|
+
key: f.string({ min: 1, desc: "Option name returned as the answer" }),
|
|
8623
|
+
description: f.string({
|
|
8624
|
+
min: 1,
|
|
8625
|
+
desc: "What choosing this option means, concretely"
|
|
8626
|
+
})
|
|
8627
|
+
}),
|
|
8628
|
+
{ desc: "choice only: the options (2-255). Option order can move the answer." }
|
|
8629
|
+
)
|
|
8630
|
+
),
|
|
8631
|
+
levels: f.optional(
|
|
8632
|
+
f.array(f.string({ min: 1 }), {
|
|
8633
|
+
desc: "score only: 2-10 level descriptions, lowest first"
|
|
8634
|
+
})
|
|
8635
|
+
)
|
|
8636
|
+
},
|
|
8637
|
+
{ desc: "One typed question" }
|
|
8638
|
+
);
|
|
8639
|
+
var stateDesc = "The thing being judged, as plain text or JSON. Required unless items is given; with items it is ignored.";
|
|
8640
|
+
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.";
|
|
8641
|
+
var questionsDesc = "1-32 questions, all answered independently against the same state.";
|
|
8642
|
+
var evaluateQuestionsContract = defineToolContract({
|
|
8643
|
+
name: "evaluate_questions",
|
|
8644
|
+
agent: {
|
|
8645
|
+
description: DESCRIPTION,
|
|
8646
|
+
fields: {
|
|
8647
|
+
state: f.optional(f.string({ desc: stateDesc })),
|
|
8648
|
+
questions: f.array(question, { min: 1, desc: questionsDesc }),
|
|
8649
|
+
items: f.optional(f.array(f.string(), { desc: itemsDesc }))
|
|
8650
|
+
}
|
|
8651
|
+
},
|
|
8652
|
+
mcp: {
|
|
8653
|
+
description: `${DESCRIPTION} Pass projectId to target a specific project; otherwise the configured default project is used.`,
|
|
8654
|
+
fields: {
|
|
8655
|
+
projectId: mcpProjectId,
|
|
8656
|
+
state: f.optional(f.string({ desc: stateDesc })),
|
|
8657
|
+
questions: f.array(question, { min: 1, desc: questionsDesc }),
|
|
8658
|
+
items: f.optional(f.array(f.string(), { desc: itemsDesc }))
|
|
8659
|
+
}
|
|
8660
|
+
}
|
|
8661
|
+
});
|
|
8662
|
+
var evaluateContracts = [evaluateQuestionsContract];
|
|
8570
8663
|
var TOOL_CONTRACTS = Object.fromEntries(
|
|
8571
8664
|
[
|
|
8572
8665
|
...tasksContracts,
|
|
@@ -8583,7 +8676,8 @@ var TOOL_CONTRACTS = Object.fromEntries(
|
|
|
8583
8676
|
...driveContracts,
|
|
8584
8677
|
...meetingsContracts,
|
|
8585
8678
|
...logsContracts,
|
|
8586
|
-
...ciWaitContracts
|
|
8679
|
+
...ciWaitContracts,
|
|
8680
|
+
...evaluateContracts
|
|
8587
8681
|
].map((contract) => [contract.name, contract])
|
|
8588
8682
|
);
|
|
8589
8683
|
|
|
@@ -9588,6 +9682,7 @@ function buildSetManualTestsTool(connection) {
|
|
|
9588
9682
|
});
|
|
9589
9683
|
const parts = [`Created ${result.created} manual test item(s).`];
|
|
9590
9684
|
if (result.skipped > 0) parts.push(`Skipped ${result.skipped} duplicate(s).`);
|
|
9685
|
+
for (const item of result.rejected ?? []) parts.push(describeManualTestRejection(item));
|
|
9591
9686
|
return textResult(parts.join(" "));
|
|
9592
9687
|
} catch (error) {
|
|
9593
9688
|
const msg = error instanceof Error ? error.message : "Unknown error";
|
|
@@ -10149,9 +10244,9 @@ async function rejectBadContextPaths(contextPaths, workspaceDir) {
|
|
|
10149
10244
|
function buildListTagsTool(connection, projectId) {
|
|
10150
10245
|
return defineContractTool(
|
|
10151
10246
|
listTagsContract,
|
|
10152
|
-
async () => {
|
|
10247
|
+
async ({ detail }) => {
|
|
10153
10248
|
try {
|
|
10154
|
-
const tags = await connection.call("listProjectTags", { projectId });
|
|
10249
|
+
const tags = await connection.call("listProjectTags", { projectId, detail });
|
|
10155
10250
|
return textResult(JSON.stringify(tags, null, 2));
|
|
10156
10251
|
} catch (error) {
|
|
10157
10252
|
return errText("Failed to list tags", error);
|
|
@@ -11016,6 +11111,31 @@ function settleTools(connection, projectId) {
|
|
|
11016
11111
|
];
|
|
11017
11112
|
}
|
|
11018
11113
|
|
|
11114
|
+
// src/tools/evaluate-tools.ts
|
|
11115
|
+
function buildEvaluateTools(connection, projectId) {
|
|
11116
|
+
return [
|
|
11117
|
+
defineContractTool(
|
|
11118
|
+
evaluateQuestionsContract,
|
|
11119
|
+
async (input) => {
|
|
11120
|
+
try {
|
|
11121
|
+
const res = await connection.call("evaluateQuestions", {
|
|
11122
|
+
projectId,
|
|
11123
|
+
state: input.state,
|
|
11124
|
+
items: input.items,
|
|
11125
|
+
questions: input.questions
|
|
11126
|
+
});
|
|
11127
|
+
return textResult(formatEvaluateQuestionsResult(res));
|
|
11128
|
+
} catch (error) {
|
|
11129
|
+
return textResult(
|
|
11130
|
+
`evaluate_questions failed: ${error instanceof Error ? error.message : "Unknown error"}`
|
|
11131
|
+
);
|
|
11132
|
+
}
|
|
11133
|
+
},
|
|
11134
|
+
{ annotations: { readOnlyHint: true } }
|
|
11135
|
+
)
|
|
11136
|
+
];
|
|
11137
|
+
}
|
|
11138
|
+
|
|
11019
11139
|
// src/tools/connected-tools.ts
|
|
11020
11140
|
function connectedToolsFor(connection, context) {
|
|
11021
11141
|
const projectId = context?.projectId;
|
|
@@ -11028,6 +11148,9 @@ function connectedToolsFor(connection, context) {
|
|
|
11028
11148
|
// Always available: the decision tables exist on every project, and a fork
|
|
11029
11149
|
// an agent cannot file is a fork it assumes its way past instead.
|
|
11030
11150
|
...buildDecisionTools(connection, projectId),
|
|
11151
|
+
// Ungated: whether the project has Jev on is answered by calling it, and
|
|
11152
|
+
// the error names what an admin has to switch on.
|
|
11153
|
+
...buildEvaluateTools(connection, projectId),
|
|
11031
11154
|
...context.googleAnalyticsConfigured ? [buildAnalyticsSummaryTool(connection, projectId)] : [],
|
|
11032
11155
|
...context.gcpLogsConfigured ? [buildQueryGcpLogsTool(connection, projectId)] : [],
|
|
11033
11156
|
...context.grafanaLogsConfigured ? [buildQueryGrafanaLogsTool(connection, projectId)] : []
|
|
@@ -11035,8 +11158,6 @@ function connectedToolsFor(connection, context) {
|
|
|
11035
11158
|
}
|
|
11036
11159
|
|
|
11037
11160
|
// src/tools/code-review-tools.ts
|
|
11038
|
-
import { execFile } from "child_process";
|
|
11039
|
-
import { promisify } from "util";
|
|
11040
11161
|
import { z as z10 } from "zod";
|
|
11041
11162
|
async function endReviewSession(connection, reason) {
|
|
11042
11163
|
await connection.call("endReviewSession", {
|
|
@@ -11047,101 +11168,6 @@ async function endReviewSession(connection, reason) {
|
|
|
11047
11168
|
var RISK_LEVELS = ["critical", "high", "medium", "low"];
|
|
11048
11169
|
var reviewedShaSchema = z10.string().regex(/^[0-9a-f]{40}$/i).describe("REQUIRED. The full 40-character commit SHA this verdict reviews.");
|
|
11049
11170
|
var riskDescription = "REQUIRED. The risk level this change carries, judged by the surface area it touches: critical = touches critical/foundational surface, high = important surface, medium = moderate, low = small/isolated. Set this on every verdict. You have authority to override a risk level already set on the task if you disagree with it.";
|
|
11050
|
-
var ReviewGuideToolSchema = z10.strictObject({
|
|
11051
|
-
reviewedSha: z10.string().regex(/^[0-9a-f]{40}$/i).describe(
|
|
11052
|
-
"REQUIRED. The PR's current head as a full 40-char SHA. Run `git rev-parse HEAD` immediately before this call \u2014 never extend an abbreviated hash into 40 characters."
|
|
11053
|
-
),
|
|
11054
|
-
overview: z10.string().min(1).max(6e4).describe("REQUIRED. Plain-text walkthrough intro, max 3000 characters. Keep it short."),
|
|
11055
|
-
sections: z10.array(
|
|
11056
|
-
z10.strictObject({
|
|
11057
|
-
title: z10.string().min(1).max(160),
|
|
11058
|
-
explanation: z10.string().min(1).max(2e3),
|
|
11059
|
-
classification: z10.enum(["core", "supporting"]).optional(),
|
|
11060
|
-
files: z10.array(
|
|
11061
|
-
z10.strictObject({
|
|
11062
|
-
path: z10.string().min(1).max(500).describe(
|
|
11063
|
-
"A file the PR's diff actually changed. Context files you merely read are rejected."
|
|
11064
|
-
),
|
|
11065
|
-
startLine: z10.number().int().positive().max(1e6).optional(),
|
|
11066
|
-
endLine: z10.number().int().positive().max(1e6).optional(),
|
|
11067
|
-
hunkHeader: z10.string().min(1).max(300).optional().describe(
|
|
11068
|
-
"Optional anchor, matched byte-exactly against the full hunk header line from `git diff` INCLUDING the context text after the second @@. Copy it verbatim from `git diff <base>..HEAD -- <file> | grep '^@@'`, or omit anchors entirely (path-only entries always validate)."
|
|
11069
|
-
)
|
|
11070
|
-
})
|
|
11071
|
-
).min(1).max(20)
|
|
11072
|
-
})
|
|
11073
|
-
).min(1).max(12).optional().describe(
|
|
11074
|
-
"REQUIRED top-level array (never text appended to overview) of ordered conceptual sections."
|
|
11075
|
-
)
|
|
11076
|
-
});
|
|
11077
|
-
var FLATTENED_SECTIONS_PATTERN = /<\/overview>\s*(?:<parameter name="sections">|<sections>)/;
|
|
11078
|
-
var TRAILING_TAGS_PATTERN = /(?:\s*<\/(?:parameter|sections|invoke)>)+\s*$/;
|
|
11079
|
-
function sliceOutermostArray(tail) {
|
|
11080
|
-
const start = tail.indexOf("[");
|
|
11081
|
-
const end = tail.lastIndexOf("]");
|
|
11082
|
-
return start >= 0 && end > start ? tail.slice(start, end + 1) : null;
|
|
11083
|
-
}
|
|
11084
|
-
function recoverFlattenedGuide(overview) {
|
|
11085
|
-
const match = FLATTENED_SECTIONS_PATTERN.exec(overview);
|
|
11086
|
-
if (!match) return null;
|
|
11087
|
-
const head = overview.slice(0, match.index).trim();
|
|
11088
|
-
const tail = overview.slice(match.index + match[0].length).replace(TRAILING_TAGS_PATTERN, "").trim();
|
|
11089
|
-
for (const candidate of [tail, sliceOutermostArray(tail)]) {
|
|
11090
|
-
if (!candidate) continue;
|
|
11091
|
-
try {
|
|
11092
|
-
return { overview: head, sections: JSON.parse(candidate) };
|
|
11093
|
-
} catch {
|
|
11094
|
-
}
|
|
11095
|
-
}
|
|
11096
|
-
return null;
|
|
11097
|
-
}
|
|
11098
|
-
async function resolveGitHeadSha(cwd) {
|
|
11099
|
-
try {
|
|
11100
|
-
const stdout = workbenchEnabled() ? (await getWorkbenchClient().execFile("git", ["rev-parse", "HEAD"], {
|
|
11101
|
-
cwd,
|
|
11102
|
-
timeout: 1e4
|
|
11103
|
-
})).stdout : (await promisify(execFile)("git", ["rev-parse", "HEAD"], { cwd, timeout: 1e4 })).stdout;
|
|
11104
|
-
const sha = stdout.trim();
|
|
11105
|
-
return /^[0-9a-f]{40}$/i.test(sha) ? sha : null;
|
|
11106
|
-
} catch {
|
|
11107
|
-
return null;
|
|
11108
|
-
}
|
|
11109
|
-
}
|
|
11110
|
-
function buildPublishReviewGuideTool(connection, options = {}) {
|
|
11111
|
-
const { resolveHeadSha } = options;
|
|
11112
|
-
return defineTool(
|
|
11113
|
-
"publish_review_guide",
|
|
11114
|
-
"Publish or update the PR guide for the CURRENT PR head commit. Call right after create_pull_request succeeds, and again after every push to the branch. The head SHA is resolved from the local repo, so never hand-expand a short hash. Best-effort \u2014 it does not gate opening or updating the PR.",
|
|
11115
|
-
ReviewGuideToolSchema.shape,
|
|
11116
|
-
async ({ reviewedSha, overview, sections }) => {
|
|
11117
|
-
let resolvedOverview = overview;
|
|
11118
|
-
let resolvedSections = sections;
|
|
11119
|
-
if (resolvedSections === void 0) {
|
|
11120
|
-
const recovered = recoverFlattenedGuide(overview);
|
|
11121
|
-
if (!recovered) {
|
|
11122
|
-
throw new Error(
|
|
11123
|
-
"publish_review_guide requires a top-level `sections` array \u2014 an ordered list of conceptual sections, each with title, explanation, and files[]. Your call arrived with only `reviewedSha` and `overview`; the `sections` parameter never reached the server, which usually means the call encoding flattened it into `overview`. Do not append the sections to `overview` as text. Retry with sections as a real array argument, and shorten `overview` if the failure repeats."
|
|
11124
|
-
);
|
|
11125
|
-
}
|
|
11126
|
-
resolvedOverview = recovered.overview;
|
|
11127
|
-
resolvedSections = recovered.sections;
|
|
11128
|
-
}
|
|
11129
|
-
const content = ReviewGuideContentSchema.parse({
|
|
11130
|
-
overview: resolvedOverview,
|
|
11131
|
-
sections: resolvedSections
|
|
11132
|
-
});
|
|
11133
|
-
const result = await connection.call("publishReviewGuide", {
|
|
11134
|
-
sessionId: connection.sessionId,
|
|
11135
|
-
reviewedSha: (resolveHeadSha ? await resolveHeadSha() : null) ?? reviewedSha,
|
|
11136
|
-
...content
|
|
11137
|
-
});
|
|
11138
|
-
return textResult(
|
|
11139
|
-
`Review guide published for ${result.reviewedSha}${result.replaced ? " (replaced)" : ""}.`
|
|
11140
|
-
);
|
|
11141
|
-
},
|
|
11142
|
-
{ strict: true }
|
|
11143
|
-
);
|
|
11144
|
-
}
|
|
11145
11171
|
function buildApproveCodeReviewTool(connection) {
|
|
11146
11172
|
return defineTool(
|
|
11147
11173
|
"approve_code_review",
|
|
@@ -11251,15 +11277,6 @@ function getModeTools(agentMode, connection, config, context) {
|
|
|
11251
11277
|
return config.mode === "pm" ? buildPmTools(connection) : [];
|
|
11252
11278
|
}
|
|
11253
11279
|
}
|
|
11254
|
-
function buildPrGuideToolsFor(effectiveMode, connection, config, context) {
|
|
11255
|
-
const isLeafBuild = effectiveMode === "building" || effectiveMode === "auto" || effectiveMode === "chat";
|
|
11256
|
-
const isPackRunner = config.mode === "pack";
|
|
11257
|
-
return (isLeafBuild || isPackRunner) && (isPackRunner || !context?.isParentTask) ? [
|
|
11258
|
-
buildPublishReviewGuideTool(connection, {
|
|
11259
|
-
resolveHeadSha: () => resolveGitHeadSha(config.workspaceDir)
|
|
11260
|
-
})
|
|
11261
|
-
] : [];
|
|
11262
|
-
}
|
|
11263
11280
|
function ciWaitToolsFor(effectiveMode, connection, config) {
|
|
11264
11281
|
const isPackRunner = config.mode === "pack";
|
|
11265
11282
|
return effectiveMode === "review" && !isPackRunner ? [] : [buildWaitForChecksTool(connection)];
|
|
@@ -11294,8 +11311,6 @@ var ALWAYS_LOADED_TOOLS = /* @__PURE__ */ new Set([
|
|
|
11294
11311
|
// The shared-skill card write: one name whose fields match the mcp surface,
|
|
11295
11312
|
// so a single skill text drives a local session and a pod verbatim.
|
|
11296
11313
|
"update_task",
|
|
11297
|
-
// Building/auto — the PR guide is published as part of opening the PR
|
|
11298
|
-
"publish_review_guide",
|
|
11299
11314
|
// Review mode
|
|
11300
11315
|
"approve_code_review",
|
|
11301
11316
|
"request_code_changes"
|
|
@@ -11349,7 +11364,6 @@ function buildConveyorTools(connection, config, context, agentMode) {
|
|
|
11349
11364
|
const modeTools = getModeTools(effectiveMode, connection, config, context);
|
|
11350
11365
|
const discoveryTools = effectiveMode === "discovery" || effectiveMode === "auto" || effectiveMode === "building" || effectiveMode === "chat" ? buildDiscoveryTools(connection) : [];
|
|
11351
11366
|
const codeReviewTools = effectiveMode === "review" ? buildCodeReviewTools(connection) : [];
|
|
11352
|
-
const prGuideTools = buildPrGuideToolsFor(effectiveMode, connection, config, context);
|
|
11353
11367
|
const ciWaitTools = ciWaitToolsFor(effectiveMode, connection, config);
|
|
11354
11368
|
const handoffTools = config.mode === "pm" && (effectiveMode === "discovery" || effectiveMode === "auto") ? [buildHandoffTool(connection)] : [];
|
|
11355
11369
|
const emergencyTools = [buildForceUpdateTaskStatusTool(connection)];
|
|
@@ -11362,7 +11376,6 @@ function buildConveyorTools(connection, config, context, agentMode) {
|
|
|
11362
11376
|
...modeTools,
|
|
11363
11377
|
...discoveryTools,
|
|
11364
11378
|
...codeReviewTools,
|
|
11365
|
-
...prGuideTools,
|
|
11366
11379
|
...ciWaitTools,
|
|
11367
11380
|
...handoffTools,
|
|
11368
11381
|
...glossaryTools,
|
|
@@ -13927,9 +13940,9 @@ function buildUnmeasurableEvent(reason, codingAgentKeyId) {
|
|
|
13927
13940
|
}
|
|
13928
13941
|
|
|
13929
13942
|
// src/runner/parent-pull-handler.ts
|
|
13930
|
-
import { execFile
|
|
13931
|
-
import { promisify
|
|
13932
|
-
var execFileAsync =
|
|
13943
|
+
import { execFile } from "child_process";
|
|
13944
|
+
import { promisify } from "util";
|
|
13945
|
+
var execFileAsync = promisify(execFile);
|
|
13933
13946
|
async function handlePullBranch(workDir, branch) {
|
|
13934
13947
|
if (!branch) return;
|
|
13935
13948
|
const current = await getCurrentBranch(workDir);
|
package/dist/cli.js
CHANGED
|
@@ -7,7 +7,7 @@ import {
|
|
|
7
7
|
WorkspaceCommandSupervisor,
|
|
8
8
|
startWorkspaceCommandsAfterConnect,
|
|
9
9
|
stopWorkspaceCommands
|
|
10
|
-
} from "./chunk-
|
|
10
|
+
} from "./chunk-DMTBXIA4.js";
|
|
11
11
|
import {
|
|
12
12
|
PtyHarness,
|
|
13
13
|
SessionRunner,
|
|
@@ -25,7 +25,7 @@ import {
|
|
|
25
25
|
runUsageProbe,
|
|
26
26
|
sampleKeyUsage,
|
|
27
27
|
usesNativeUsageReporting
|
|
28
|
-
} from "./chunk-
|
|
28
|
+
} from "./chunk-ND5INHM4.js";
|
|
29
29
|
import "./chunk-37J5MMQT.js";
|
|
30
30
|
import {
|
|
31
31
|
inheritedEnv,
|
|
@@ -44,7 +44,7 @@ import {
|
|
|
44
44
|
createServiceLogger,
|
|
45
45
|
fetchBootstrap,
|
|
46
46
|
loadConveyorConfig
|
|
47
|
-
} from "./chunk-
|
|
47
|
+
} from "./chunk-GHNYP67H.js";
|
|
48
48
|
import "./chunk-Q4FQOJ7D.js";
|
|
49
49
|
import "./chunk-W4LZ7R6Z.js";
|
|
50
50
|
import "./chunk-372R6E4C.js";
|
|
@@ -1458,7 +1458,7 @@ if (!RUNNER_MODES.includes(CONVEYOR_MODE)) {
|
|
|
1458
1458
|
process.exit(1);
|
|
1459
1459
|
}
|
|
1460
1460
|
if (CONVEYOR_MODE === "serving") {
|
|
1461
|
-
const { runServingSession } = await import("./serve-boot-
|
|
1461
|
+
const { runServingSession } = await import("./serve-boot-CYVW7Z27.js");
|
|
1462
1462
|
exitContext.runnerMode = "serving";
|
|
1463
1463
|
exitContext.sessionId = process.env.CONVEYOR_SESSION_ID ?? exitContext.sessionId;
|
|
1464
1464
|
const outcome = await runServingSession({
|
package/dist/index.js
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import {
|
|
2
2
|
SessionRunner,
|
|
3
3
|
unshallowRepo
|
|
4
|
-
} from "./chunk-
|
|
4
|
+
} from "./chunk-ND5INHM4.js";
|
|
5
5
|
import "./chunk-37J5MMQT.js";
|
|
6
6
|
import "./chunk-WCJ6HRVU.js";
|
|
7
7
|
import {
|
|
@@ -17,7 +17,7 @@ import {
|
|
|
17
17
|
stageAndCommit,
|
|
18
18
|
updateRemoteToken,
|
|
19
19
|
workspacePathExists
|
|
20
|
-
} from "./chunk-
|
|
20
|
+
} from "./chunk-GHNYP67H.js";
|
|
21
21
|
import "./chunk-Q4FQOJ7D.js";
|
|
22
22
|
import {
|
|
23
23
|
runAuthTokenCommand,
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import {
|
|
2
2
|
WorkspaceCommandSupervisor
|
|
3
|
-
} from "./chunk-
|
|
3
|
+
} from "./chunk-DMTBXIA4.js";
|
|
4
4
|
import {
|
|
5
5
|
AgentConnection,
|
|
6
6
|
CodespacePortVisibility,
|
|
@@ -11,7 +11,7 @@ import {
|
|
|
11
11
|
createServiceLogger,
|
|
12
12
|
ensureOnTaskBranch,
|
|
13
13
|
loadConveyorConfig
|
|
14
|
-
} from "./chunk-
|
|
14
|
+
} from "./chunk-GHNYP67H.js";
|
|
15
15
|
import "./chunk-Q4FQOJ7D.js";
|
|
16
16
|
import "./chunk-W4LZ7R6Z.js";
|
|
17
17
|
import "./chunk-IA45XHOA.js";
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@rallycry/conveyor-agent",
|
|
3
|
-
"version": "11.0.
|
|
3
|
+
"version": "11.0.28",
|
|
4
4
|
"description": "Conveyor Agent Runner v10 - PTY harness for the task chat (SDK harness for audit/project-chat). Agent-as-User architecture with BaseService patterns. Works locally too.",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"agent",
|
|
@@ -206,6 +206,15 @@ capture a screenshot (static) or a short recording (interaction) with the host
|
|
|
206
206
|
repo's own tooling and attach it with `mcp__conveyor__upload_attachment`, then
|
|
207
207
|
embed the returned URL in the PR body. A UI PR without it is incomplete.
|
|
208
208
|
|
|
209
|
+
**Manual tests are for human eyes, and few.** Before the PR, record with
|
|
210
|
+
`mcp__conveyor__set_manual_tests` only what a person must see in the running
|
|
211
|
+
app to sign the card off: at most 5, usually 1, none for a change with no
|
|
212
|
+
user-visible behavior. One sentence each, in plain language: where to go, what
|
|
213
|
+
to do, what they should see ("Open any card with a PR: the Pull Request slat
|
|
214
|
+
sits directly under Description"). Never record gates, commands, CI, logs, or
|
|
215
|
+
database steps; the server rejects them. A rejected item is a signal to drop
|
|
216
|
+
it, not to rephrase it.
|
|
217
|
+
|
|
209
218
|
**On a deep pack the gate surface grows, and that is expected.**
|
|
210
219
|
`test:affected` diffs against `origin/dev`, so by the fourth child it also
|
|
211
220
|
covers the three already merged into the pack branch. That is correct
|
|
@@ -276,17 +285,7 @@ nothing invalidates the verification you just did:
|
|
|
276
285
|
recorded on the card. That fallback is usually right and is exactly why the
|
|
277
286
|
mistake is invisible — until the card's recorded branch has drifted, and the
|
|
278
287
|
PR opens against `dev`.
|
|
279
|
-
4.
|
|
280
|
-
after `create_pull_request` succeeds — it is part of opening a PR, not a
|
|
281
|
-
follow-up. `sections` is a top-level array argument, not prose stuffed into
|
|
282
|
-
`overview`; keep `overview` a short intro and order the sections with core
|
|
283
|
-
behavior first, tests and generated files later. Reference only files this
|
|
284
|
-
diff actually changed, never context you merely read. Anchors are optional
|
|
285
|
-
and strict — `{"path": "..."}` alone is the safe form. Republish for the new
|
|
286
|
-
head SHA after any later push, or the card's Guide tab shows stale. It is
|
|
287
|
-
best-effort: if it still fails after one corrected retry, carry on — it
|
|
288
|
-
never blocks opening or updating the PR.
|
|
289
|
-
5. Post a chat summary: what shipped, how it was verified, what a reviewer
|
|
288
|
+
4. Post a chat summary: what shipped, how it was verified, what a reviewer
|
|
290
289
|
should look at, and anything you did NOT do.
|
|
291
290
|
|
|
292
291
|
Then confirm CI actually started (read-only `gh pr checks`). Do not wait on it.
|
|
@@ -21,7 +21,7 @@ The genre: **an uninterested observer pulls every relevant source, counts what a
|
|
|
21
21
|
1. `get_connection_context` — who and where. Same call in a pod and a local MCP session; it reports the card in a pod and the account/board in an MCP client.
|
|
22
22
|
2. `list_project_integrations` — which of repository, Slack/Discord, email, GCP, Grafana, Google Analytics, Drive, Cloudflare, incidents are configured, plus the registered work channels. Credential-free booleans; this is the map.
|
|
23
23
|
3. `list_project_channels` — the channels an admin registered as agent-reachable, each with a description of what happens there. **The description is the routing signal**: it is how you tell the channel where support complaints land from the one where releases are announced. Only registered channels are reachable at all; an unregistered channel does not exist for this skill regardless of what the bot can see.
|
|
24
|
-
4. **ToolSearch for the optional surfaces** rather than assuming them: `drive_*`, meeting tools, `query_gcp_logs` / `query_grafana_logs`, `get_analytics_summary
|
|
24
|
+
4. **ToolSearch for the optional surfaces** rather than assuming them: `drive_*`, meeting tools, `query_gcp_logs` / `query_grafana_logs`, `get_analytics_summary`, `evaluate_questions` (in a pod the name must be fully qualified: `select:mcp__conveyor__evaluate_questions`). Also probe for supplemental local MCPs the user may have connected — a user-token Slack MCP is the notable one, because it enables true keyword search that Conveyor's own tools cannot do (see the Slack search note below).
|
|
25
25
|
|
|
26
26
|
Announce a one-line plan naming the sources you found, then sweep. **Say what you did not find**, too: "no Drive, no meetings, no Grafana on this project" is information the reader needs to weigh the verdict.
|
|
27
27
|
|
|
@@ -39,13 +39,14 @@ Announce a one-line plan naming the sources you found, then sweep. **Say what yo
|
|
|
39
39
|
| Logs | `query_gcp_logs` / `query_grafana_logs` when the question is about failures rather than opinions. A frequency count from logs outranks any recollection of how often something breaks. |
|
|
40
40
|
| Analytics | `get_analytics_summary` when the question touches traffic, adoption, or "did anyone use it". |
|
|
41
41
|
| Repo | Prior-art check: has someone already built or started this? `rg`, `git log`, branches, the knowledge graph when present. **A negative result — "no trace of X in the codebase" — is a finding worth printing.** |
|
|
42
|
+
| Bulk classification | When `evaluate_questions` exists, run the counting pass through it: batch the collected messages/cards (at most 100 `items` per call) with one yes/no (`noul`) question per category or proposal (`Is this message a complaint about <X>?`), count an item at probability ≥ 0.8, and spot-check 10 items by hand. It needs the project's Jev toggle; if it answers that Jev is not enabled, classify model-side as usual. Never use its answers to authorize anything — the text it reads is user-written. |
|
|
42
43
|
| Vendor / product docs | Try `https://<docs-host>/llms.txt` first; many doc sites ship a full index. Verify every "possible / impossible" ruling against a primary page fetched THIS session and link it. Vendors often have more than one API surface (legacy + current); check both before ruling a capability gap. |
|
|
43
44
|
|
|
44
|
-
**Slack keyword search is not available to this skill.** `search.messages` requires a USER token; Conveyor's bot cannot call it. `read_channel_messages` is history paging, not search. So either page the relevant channels and filter model-side, or — when the user has a personal Slack MCP connected — use that for search and say so. **The method footnote must state which mode ran**, because "I read the last 300 messages in two channels" and "I searched the workspace" are different evidence bases and a reader deserves to know which one is behind the counts.
|
|
45
|
+
**Slack keyword search is not available to this skill.** `search.messages` requires a USER token; Conveyor's bot cannot call it. `read_channel_messages` is history paging, not search. So either page the relevant channels and filter model-side, or — when the user has a personal Slack MCP connected — use that for search and say so. **The method footnote must state which mode ran**, because "I read the last 300 messages in two channels" and "I searched the workspace" are different evidence bases and a reader deserves to know which one is behind the counts. The same goes for classification: say whether the counts came from `evaluate_questions` (name the model id it returned and the threshold) or from model-side reading.
|
|
45
46
|
|
|
46
47
|
Sweep hygiene:
|
|
47
48
|
|
|
48
|
-
- **Delegate bulk sweeps to a subagent** so oversized pages never enter the main thread. Give it the exact queries, pagination instructions, the tool-loading line, and a strict deliverable spec: categorized findings with counts, sources, a few dated example incidents each with a link, a raw-coverage note (queries run, volume reviewed, date range, dominant sources), and a surprises section. Cap the report length.
|
|
49
|
+
- **Delegate bulk sweeps to a subagent** so oversized pages never enter the main thread. Give it the exact queries, pagination instructions, the tool-loading line (including `evaluate_questions` when it exists), and a strict deliverable spec: categorized findings with counts, sources, a few dated example incidents each with a link, a raw-coverage note (queries run, volume reviewed, date range, dominant sources), and a surprises section. Cap the report length.
|
|
49
50
|
- Record who reported each incident and when. Attribution splits ("their side vs ours", "process vs code defect") and time windows ("only since <month>") are the follow-up questions every single time; keep the underlying dated list so re-slicing is cheap.
|
|
50
51
|
|
|
51
52
|
## Phase 2: analysis
|
|
@@ -14,7 +14,8 @@ High-level strategy with relevant files/patterns/APIs.
|
|
|
14
14
|
2. Concrete step.
|
|
15
15
|
|
|
16
16
|
## Testing
|
|
17
|
-
-
|
|
17
|
+
- Gates: the scoped lint/typecheck/test commands.
|
|
18
|
+
- Manual tests (0-3): one plain sentence each, user path only; omit when nothing is user-visible.
|
|
18
19
|
- Edge cases.
|
|
19
20
|
|
|
20
21
|
## Notes
|
|
@@ -49,8 +50,11 @@ The executor treats it as the acceptance criteria, so enumerate:
|
|
|
49
50
|
affected-test commands — naming the specific package suites the diff will
|
|
50
51
|
touch. A docs-only plan should say plainly that no local gates are needed and
|
|
51
52
|
that CI validates on the PR.
|
|
52
|
-
-
|
|
53
|
-
|
|
53
|
+
- Manual tests, as a separate list of 0-3 items: one plain sentence each
|
|
54
|
+
naming where to go, what to do, and what a person should see. User paths
|
|
55
|
+
only; never a command, a gate, CI, or a log check. Omit the list when the
|
|
56
|
+
change has nothing user-visible. The builder copies the Manual tests list
|
|
57
|
+
into `set_manual_tests` and nothing else from this section.
|
|
54
58
|
|
|
55
59
|
A planner working read-only is not expected to RUN these. Describe them.
|
|
56
60
|
|