@rallycry/conveyor-agent 7.3.7 → 7.3.9

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -40,6 +40,8 @@ var AgentConnection = class _AgentConnection {
40
40
  softStopCallback = null;
41
41
  modeChangeCallback = null;
42
42
  apiKeyUpdateCallback = null;
43
+ pullBranchCallback = null;
44
+ earlyPullBranches = [];
43
45
  constructor(config) {
44
46
  this.config = config;
45
47
  }
@@ -130,6 +132,10 @@ var AgentConnection = class _AgentConnection {
130
132
  this.socket.on("agentRunner:updateApiKey", (data) => {
131
133
  if (this.apiKeyUpdateCallback) this.apiKeyUpdateCallback(data);
132
134
  });
135
+ this.socket.on("session:pullBranch", (data) => {
136
+ if (this.pullBranchCallback) this.pullBranchCallback(data);
137
+ else this.earlyPullBranches.push(data);
138
+ });
133
139
  this.socket.on("connect", () => {
134
140
  process.stderr.write("[conveyor-agent] Socket connected\n");
135
141
  if (!settled) {
@@ -251,6 +257,11 @@ var AgentConnection = class _AgentConnection {
251
257
  onApiKeyUpdate(callback) {
252
258
  this.apiKeyUpdateCallback = callback;
253
259
  }
260
+ onPullBranch(callback) {
261
+ this.pullBranchCallback = callback;
262
+ for (const data of this.earlyPullBranches) callback(data);
263
+ this.earlyPullBranches = [];
264
+ }
254
265
  // ── Convenience methods (thin wrappers around call / emit) ─────────
255
266
  async emitStatus(status) {
256
267
  this.lastEmittedStatus = status;
@@ -1384,6 +1395,8 @@ var DeleteSubtaskResponseSchema = z.object({
1384
1395
  });
1385
1396
  var RegisterProjectAgentResponseSchema = z.object({
1386
1397
  registered: z.boolean(),
1398
+ /** ProjectCodespaceSession id — used by the runner to filter targeted chat messages. */
1399
+ sessionId: z.string(),
1387
1400
  agentName: z.string(),
1388
1401
  agentInstructions: z.string(),
1389
1402
  model: z.string(),
@@ -1456,7 +1469,8 @@ var CreateProjectTaskRequestSchema = z2.object({
1456
1469
  title: z2.string().min(1),
1457
1470
  description: z2.string().optional(),
1458
1471
  plan: z2.string().optional(),
1459
- status: z2.string().optional()
1472
+ status: z2.string().optional(),
1473
+ requestingUserId: z2.string().optional()
1460
1474
  });
1461
1475
  var UpdateProjectTaskRequestSchema = z2.object({
1462
1476
  projectId: z2.string(),
@@ -1465,7 +1479,8 @@ var UpdateProjectTaskRequestSchema = z2.object({
1465
1479
  description: z2.string().optional(),
1466
1480
  plan: z2.string().optional(),
1467
1481
  status: z2.string().optional(),
1468
- assignedUserId: z2.string().nullish()
1482
+ assignedUserId: z2.string().nullish(),
1483
+ requestingUserId: z2.string().optional()
1469
1484
  });
1470
1485
  var ReportProjectAgentEventRequestSchema = z2.object({
1471
1486
  projectId: z2.string(),
@@ -1528,7 +1543,8 @@ var GetProjectCliHistoryRequestSchema = z2.object({
1528
1543
  var PostToProjectTaskChatRequestSchema = z2.object({
1529
1544
  projectId: z2.string(),
1530
1545
  taskId: z2.string(),
1531
- content: z2.string()
1546
+ content: z2.string(),
1547
+ requestingUserId: z2.string().optional()
1532
1548
  });
1533
1549
  var GetProjectTaskCliRequestSchema = z2.object({
1534
1550
  projectId: z2.string(),
@@ -1538,15 +1554,71 @@ var GetProjectTaskCliRequestSchema = z2.object({
1538
1554
  });
1539
1555
  var StartProjectBuildRequestSchema = z2.object({
1540
1556
  projectId: z2.string(),
1541
- taskId: z2.string()
1557
+ taskId: z2.string(),
1558
+ requestingUserId: z2.string().optional()
1542
1559
  });
1543
1560
  var StopProjectBuildRequestSchema = z2.object({
1544
1561
  projectId: z2.string(),
1545
- taskId: z2.string()
1562
+ taskId: z2.string(),
1563
+ requestingUserId: z2.string().optional()
1546
1564
  });
1547
1565
  var ApproveProjectMergePRRequestSchema = z2.object({
1548
1566
  projectId: z2.string(),
1549
- childTaskId: z2.string()
1567
+ childTaskId: z2.string(),
1568
+ requestingUserId: z2.string().optional()
1569
+ });
1570
+ var ListProjectSubtasksRequestSchema = z2.object({
1571
+ projectId: z2.string(),
1572
+ taskId: z2.string()
1573
+ });
1574
+ var CreateProjectSubtaskRequestSchema = z2.object({
1575
+ projectId: z2.string(),
1576
+ parentTaskId: z2.string(),
1577
+ title: z2.string().min(1),
1578
+ description: z2.string().optional(),
1579
+ plan: z2.string().optional(),
1580
+ ordinal: z2.number().int().nonnegative().optional(),
1581
+ storyPointValue: z2.number().int().positive().optional(),
1582
+ requestingUserId: z2.string().optional()
1583
+ });
1584
+ var UpdateProjectSubtaskRequestSchema = z2.object({
1585
+ projectId: z2.string(),
1586
+ subtaskId: z2.string(),
1587
+ title: z2.string().optional(),
1588
+ description: z2.string().optional(),
1589
+ plan: z2.string().optional(),
1590
+ status: z2.string().optional(),
1591
+ ordinal: z2.number().int().nonnegative().optional(),
1592
+ storyPointValue: z2.number().int().positive().optional(),
1593
+ requestingUserId: z2.string().optional()
1594
+ });
1595
+ var DeleteProjectSubtaskRequestSchema = z2.object({
1596
+ projectId: z2.string(),
1597
+ subtaskId: z2.string(),
1598
+ requestingUserId: z2.string().optional()
1599
+ });
1600
+ var GetProjectTaskChatRequestSchema = z2.object({
1601
+ projectId: z2.string(),
1602
+ taskId: z2.string(),
1603
+ limit: z2.number().int().positive().optional().default(20)
1604
+ });
1605
+ var AddProjectTaskDependencyRequestSchema = z2.object({
1606
+ projectId: z2.string(),
1607
+ taskId: z2.string(),
1608
+ dependsOnSlugOrId: z2.string(),
1609
+ requestingUserId: z2.string().optional()
1610
+ });
1611
+ var RemoveProjectTaskDependencyRequestSchema = z2.object({
1612
+ projectId: z2.string(),
1613
+ taskId: z2.string(),
1614
+ dependsOnSlugOrId: z2.string(),
1615
+ requestingUserId: z2.string().optional()
1616
+ });
1617
+ var VoteProjectSuggestionRequestSchema = z2.object({
1618
+ projectId: z2.string(),
1619
+ suggestionId: z2.string(),
1620
+ value: z2.union([z2.literal(1), z2.literal(-1)]),
1621
+ requestingUserId: z2.string().optional()
1550
1622
  });
1551
1623
  var StartTaskAuditRequestSchema = z3.object({
1552
1624
  projectId: z3.string(),
@@ -1566,7 +1638,8 @@ var CreateProjectSuggestionRequestSchema = z3.object({
1566
1638
  projectId: z3.string(),
1567
1639
  title: z3.string().min(1),
1568
1640
  description: z3.string().optional(),
1569
- tagNames: z3.array(z3.string()).optional()
1641
+ tagNames: z3.array(z3.string()).optional(),
1642
+ requestingUserId: z3.string().optional()
1570
1643
  });
1571
1644
  var ReportTaskAuditBatchCompleteRequestSchema = z3.object({
1572
1645
  projectId: z3.string(),
@@ -6572,6 +6645,48 @@ function readAgentVersion() {
6572
6645
  return null;
6573
6646
  }
6574
6647
 
6648
+ // src/runner/parent-pull-handler.ts
6649
+ import { execSync as execSync2 } from "child_process";
6650
+ function handlePullBranch(workDir, branch) {
6651
+ if (!branch) return;
6652
+ const current = getCurrentBranch(workDir);
6653
+ if (current !== branch) {
6654
+ process.stderr.write(
6655
+ `[conveyor-agent] pull_branch ignored \u2014 current branch ${current ?? "(detached)"} != ${branch}
6656
+ `
6657
+ );
6658
+ return;
6659
+ }
6660
+ if (hasUncommittedChanges(workDir)) {
6661
+ process.stderr.write(
6662
+ `[conveyor-agent] pull_branch ignored \u2014 uncommitted changes on ${branch}
6663
+ `
6664
+ );
6665
+ return;
6666
+ }
6667
+ try {
6668
+ execSync2(`git fetch origin ${branch}`, { cwd: workDir, stdio: "ignore", timeout: 6e4 });
6669
+ } catch {
6670
+ process.stderr.write(`[conveyor-agent] pull_branch: fetch failed for ${branch}
6671
+ `);
6672
+ return;
6673
+ }
6674
+ try {
6675
+ execSync2(`git pull --ff-only origin ${branch}`, {
6676
+ cwd: workDir,
6677
+ stdio: "ignore",
6678
+ timeout: 6e4
6679
+ });
6680
+ process.stderr.write(`[conveyor-agent] pull_branch: pulled origin/${branch}
6681
+ `);
6682
+ } catch {
6683
+ process.stderr.write(
6684
+ `[conveyor-agent] pull_branch: ff-only pull failed for ${branch} (likely diverged)
6685
+ `
6686
+ );
6687
+ }
6688
+ }
6689
+
6575
6690
  // src/runner/session-runner.ts
6576
6691
  var SessionRunner = class _SessionRunner {
6577
6692
  connection;
@@ -7134,6 +7249,9 @@ var SessionRunner = class _SessionRunner {
7134
7249
  delete process.env.CLAUDE_CODE_OAUTH_TOKEN;
7135
7250
  }
7136
7251
  });
7252
+ this.connection.onPullBranch(({ branch }) => {
7253
+ handlePullBranch(this.config.workspaceDir, branch);
7254
+ });
7137
7255
  }
7138
7256
  /**
7139
7257
  * Rehydrate CostTracker from server. Caps at 3s so a slow API call never
@@ -7417,7 +7535,7 @@ var ProjectConnection = class {
7417
7535
  };
7418
7536
 
7419
7537
  // src/runner/commit-watcher.ts
7420
- import { execSync as execSync2 } from "child_process";
7538
+ import { execSync as execSync3 } from "child_process";
7421
7539
  var logger5 = createServiceLogger("CommitWatcher");
7422
7540
  var CommitWatcher = class {
7423
7541
  constructor(config, callbacks) {
@@ -7451,7 +7569,7 @@ var CommitWatcher = class {
7451
7569
  this.isSyncing = false;
7452
7570
  }
7453
7571
  getLocalHeadSha() {
7454
- return execSync2("git rev-parse HEAD", {
7572
+ return execSync3("git rev-parse HEAD", {
7455
7573
  cwd: this.config.projectDir,
7456
7574
  stdio: ["ignore", "pipe", "ignore"]
7457
7575
  }).toString().trim();
@@ -7459,12 +7577,12 @@ var CommitWatcher = class {
7459
7577
  poll() {
7460
7578
  if (!this.branch || this.isSyncing) return;
7461
7579
  try {
7462
- execSync2(`git fetch origin ${this.branch} --quiet`, {
7580
+ execSync3(`git fetch origin ${this.branch} --quiet`, {
7463
7581
  cwd: this.config.projectDir,
7464
7582
  stdio: "ignore",
7465
7583
  timeout: 3e4
7466
7584
  });
7467
- const remoteSha = execSync2(`git rev-parse origin/${this.branch}`, {
7585
+ const remoteSha = execSync3(`git rev-parse origin/${this.branch}`, {
7468
7586
  cwd: this.config.projectDir,
7469
7587
  stdio: ["ignore", "pipe", "ignore"]
7470
7588
  }).toString().trim();
@@ -7485,12 +7603,12 @@ var CommitWatcher = class {
7485
7603
  let latestMessage = "";
7486
7604
  let latestAuthor = "";
7487
7605
  try {
7488
- const countOutput = execSync2(`git rev-list --count ${previousSha}..origin/${this.branch}`, {
7606
+ const countOutput = execSync3(`git rev-list --count ${previousSha}..origin/${this.branch}`, {
7489
7607
  cwd: this.config.projectDir,
7490
7608
  stdio: ["ignore", "pipe", "ignore"]
7491
7609
  }).toString().trim();
7492
7610
  commitCount = parseInt(countOutput, 10) || 1;
7493
- const logOutput = execSync2(`git log -1 --format="%s|||%an" origin/${this.branch}`, {
7611
+ const logOutput = execSync3(`git log -1 --format="%s|||%an" origin/${this.branch}`, {
7494
7612
  cwd: this.config.projectDir,
7495
7613
  stdio: ["ignore", "pipe", "ignore"]
7496
7614
  }).toString().trim();
@@ -7525,7 +7643,7 @@ var CommitWatcher = class {
7525
7643
  };
7526
7644
 
7527
7645
  // src/runner/worktree.ts
7528
- import { execSync as execSync3 } from "child_process";
7646
+ import { execSync as execSync4 } from "child_process";
7529
7647
  import { existsSync as existsSync2 } from "fs";
7530
7648
  import { join as join4 } from "path";
7531
7649
  var WORKTREE_DIR = ".worktrees";
@@ -7540,7 +7658,7 @@ function ensureWorktree(projectDir, taskId, branch) {
7540
7658
  return worktreePath;
7541
7659
  }
7542
7660
  try {
7543
- execSync3(`git checkout --detach origin/${branch}`, {
7661
+ execSync4(`git checkout --detach origin/${branch}`, {
7544
7662
  cwd: worktreePath,
7545
7663
  stdio: "ignore"
7546
7664
  });
@@ -7550,7 +7668,7 @@ function ensureWorktree(projectDir, taskId, branch) {
7550
7668
  return worktreePath;
7551
7669
  }
7552
7670
  const ref = branch ? `origin/${branch}` : "HEAD";
7553
- execSync3(`git worktree add --detach "${worktreePath}" ${ref}`, {
7671
+ execSync4(`git worktree add --detach "${worktreePath}" ${ref}`, {
7554
7672
  cwd: projectDir,
7555
7673
  stdio: "ignore"
7556
7674
  });
@@ -7558,7 +7676,7 @@ function ensureWorktree(projectDir, taskId, branch) {
7558
7676
  }
7559
7677
  function detachWorktreeBranch(projectDir, branch) {
7560
7678
  try {
7561
- const output = execSync3("git worktree list --porcelain", {
7679
+ const output = execSync4("git worktree list --porcelain", {
7562
7680
  cwd: projectDir,
7563
7681
  encoding: "utf-8"
7564
7682
  });
@@ -7571,7 +7689,7 @@ function detachWorktreeBranch(projectDir, branch) {
7571
7689
  const worktreePath = worktreeLine.replace("worktree ", "");
7572
7690
  if (!worktreePath.includes(`/${WORKTREE_DIR}/`)) continue;
7573
7691
  try {
7574
- execSync3("git checkout --detach", { cwd: worktreePath, stdio: "ignore" });
7692
+ execSync4("git checkout --detach", { cwd: worktreePath, stdio: "ignore" });
7575
7693
  } catch {
7576
7694
  }
7577
7695
  }
@@ -7582,7 +7700,7 @@ function removeWorktree(projectDir, taskId) {
7582
7700
  const worktreePath = join4(projectDir, WORKTREE_DIR, taskId);
7583
7701
  if (!existsSync2(worktreePath)) return;
7584
7702
  try {
7585
- execSync3(`git worktree remove "${worktreePath}" --force`, {
7703
+ execSync4(`git worktree remove "${worktreePath}" --force`, {
7586
7704
  cwd: projectDir,
7587
7705
  stdio: "ignore"
7588
7706
  });
@@ -7591,7 +7709,7 @@ function removeWorktree(projectDir, taskId) {
7591
7709
  }
7592
7710
 
7593
7711
  // src/setup/commands.ts
7594
- import { spawn, execSync as execSync4 } from "child_process";
7712
+ import { spawn, execSync as execSync5 } from "child_process";
7595
7713
  function runSetupCommand(cmd, cwd, onOutput) {
7596
7714
  return new Promise((resolve2, reject) => {
7597
7715
  const child = spawn("sh", ["-c", cmd], {
@@ -7620,7 +7738,7 @@ function runSetupCommand(cmd, cwd, onOutput) {
7620
7738
  var AUTH_TOKEN_TIMEOUT_MS = 3e4;
7621
7739
  function runAuthTokenCommand(cmd, userEmail, cwd) {
7622
7740
  try {
7623
- const output = execSync4(`${cmd} ${JSON.stringify(userEmail)}`, {
7741
+ const output = execSync5(`${cmd} ${JSON.stringify(userEmail)}`, {
7624
7742
  cwd,
7625
7743
  timeout: AUTH_TOKEN_TIMEOUT_MS,
7626
7744
  stdio: ["ignore", "pipe", "ignore"],
@@ -7650,158 +7768,645 @@ function runStartCommand(cmd, cwd, onOutput) {
7650
7768
  }
7651
7769
 
7652
7770
  // src/tools/project-tools.ts
7771
+ import { z as z14 } from "zod";
7772
+
7773
+ // src/tools/project-action-tools.ts
7653
7774
  import { z as z13 } from "zod";
7654
- function buildTaskListTools(connection) {
7775
+ var SP_DESCRIPTION3 = "Story point value (1=Common, 2=Magic, 3=Rare, 5=Unique)";
7776
+ function withRequestingUser(payload, getRequestingUserId) {
7777
+ const requestingUserId = getRequestingUserId();
7778
+ return requestingUserId ? { ...payload, requestingUserId } : payload;
7779
+ }
7780
+ function buildCreateTaskTool(connection, getRequestingUserId) {
7655
7781
  const projectId = connection.projectId;
7656
- return [
7657
- defineTool(
7658
- "list_tasks",
7659
- "List tasks in the project. Optionally filter by status or assignee.",
7660
- {
7661
- status: z13.string().optional().describe("Filter by task status"),
7662
- assigneeId: z13.string().optional().describe("Filter by assigned user ID"),
7663
- limit: z13.number().optional().describe("Max number of tasks to return (default 50)")
7664
- },
7665
- async (params) => {
7666
- try {
7667
- const tasks = await connection.call("listProjectTasks", { projectId, ...params });
7668
- return textResult(JSON.stringify(tasks, null, 2));
7669
- } catch (error) {
7670
- return textResult(
7671
- `Failed to list tasks: ${error instanceof Error ? error.message : "Unknown error"}`
7672
- );
7673
- }
7674
- },
7675
- { annotations: { readOnlyHint: true } }
7676
- ),
7677
- defineTool(
7678
- "get_project_task",
7679
- "Get detailed information about a task in this project (chat messages, child tasks, session). Project-runner scope.",
7680
- { task_id: z13.string().describe("The task ID to look up") },
7681
- async ({ task_id }) => {
7682
- try {
7683
- const task = await connection.call("getProjectTask", { projectId, taskId: task_id });
7684
- return textResult(JSON.stringify(task, null, 2));
7685
- } catch (error) {
7782
+ return defineTool(
7783
+ "create_task",
7784
+ "Create a new task in the project.",
7785
+ {
7786
+ title: z13.string().describe("Task title"),
7787
+ description: z13.string().optional().describe("Task description"),
7788
+ plan: z13.string().optional().describe("Implementation plan in markdown"),
7789
+ status: z13.string().optional().describe("Initial status (default: Planning)")
7790
+ },
7791
+ async (params) => {
7792
+ try {
7793
+ const result = await connection.call(
7794
+ "createProjectTask",
7795
+ withRequestingUser({ projectId, ...params }, getRequestingUserId)
7796
+ );
7797
+ return textResult(`Task created: ${result.slug} (ID: ${result.id})`);
7798
+ } catch (error) {
7799
+ return textResult(`Failed to create task: ${errorMessage(error)}`);
7800
+ }
7801
+ }
7802
+ );
7803
+ }
7804
+ function buildUpdateProjectTaskTool(connection, getRequestingUserId) {
7805
+ const projectId = connection.projectId;
7806
+ return defineTool(
7807
+ "update_project_task",
7808
+ "Update an existing task's title, description, plan, status, or assignee. Project-runner scope.",
7809
+ {
7810
+ task_id: z13.string().describe("The task ID to update"),
7811
+ title: z13.string().optional().describe("New title"),
7812
+ description: z13.string().optional().describe("New description"),
7813
+ plan: z13.string().optional().describe("New plan in markdown"),
7814
+ status: z13.string().optional().describe("New status"),
7815
+ assignedUserId: z13.string().nullable().optional().describe("Assign to user ID, or null to unassign")
7816
+ },
7817
+ async ({ task_id, ...fields }) => {
7818
+ try {
7819
+ await connection.call(
7820
+ "updateProjectTask",
7821
+ withRequestingUser({ projectId, taskId: task_id, ...fields }, getRequestingUserId)
7822
+ );
7823
+ return textResult("Task updated successfully.");
7824
+ } catch (error) {
7825
+ return textResult(`Failed to update task: ${errorMessage(error)}`);
7826
+ }
7827
+ }
7828
+ );
7829
+ }
7830
+ function buildUpdateTaskPlanTool(connection, getRequestingUserId) {
7831
+ const projectId = connection.projectId;
7832
+ return defineTool(
7833
+ "update_task_plan",
7834
+ "Save a plan or description to a task. Convenience wrapper around update_project_task for the common Plan-mode case.",
7835
+ {
7836
+ task_id: z13.string().describe("The task ID to update"),
7837
+ plan: z13.string().optional().describe("The task plan in markdown"),
7838
+ description: z13.string().optional().describe("Updated task description")
7839
+ },
7840
+ async ({ task_id, plan, description }) => {
7841
+ try {
7842
+ await connection.call(
7843
+ "updateProjectTask",
7844
+ withRequestingUser(
7845
+ {
7846
+ projectId,
7847
+ taskId: task_id,
7848
+ ...plan !== void 0 && { plan },
7849
+ ...description !== void 0 && { description }
7850
+ },
7851
+ getRequestingUserId
7852
+ )
7853
+ );
7854
+ return textResult("Task updated successfully.");
7855
+ } catch (error) {
7856
+ return textResult(`Failed to update task: ${errorMessage(error)}`);
7857
+ }
7858
+ }
7859
+ );
7860
+ }
7861
+ function buildForceUpdateStatusTool(connection, getRequestingUserId) {
7862
+ const projectId = connection.projectId;
7863
+ return defineTool(
7864
+ "force_update_task_status",
7865
+ "EMERGENCY ONLY: force-override a task's Kanban status. Use when an automatic transition failed and the task is wedged. Normal flow transitions status automatically.",
7866
+ {
7867
+ task_id: z13.string().describe("The task ID to update"),
7868
+ status: z13.enum(["Planning", "Open", "InProgress", "ReviewPR", "ReviewDev", "ReviewLive", "Complete"]).describe("The new status for the task")
7869
+ },
7870
+ async ({ task_id, status }) => {
7871
+ try {
7872
+ await connection.call(
7873
+ "updateProjectTask",
7874
+ withRequestingUser({ projectId, taskId: task_id, status }, getRequestingUserId)
7875
+ );
7876
+ return textResult(`Task status updated to ${status}.`);
7877
+ } catch (error) {
7878
+ return textResult(`Failed to update status: ${errorMessage(error)}`);
7879
+ }
7880
+ }
7881
+ );
7882
+ }
7883
+ function buildCreateSubtaskTool2(connection, getRequestingUserId) {
7884
+ const projectId = connection.projectId;
7885
+ return defineTool(
7886
+ "create_subtask",
7887
+ "Create a subtask under a parent task in this project. Use for breaking parent tasks into smaller pieces during planning.",
7888
+ {
7889
+ parent_task_id: z13.string().describe("The parent task ID"),
7890
+ title: z13.string().describe("Subtask title"),
7891
+ description: z13.string().optional().describe("Brief description"),
7892
+ plan: z13.string().optional().describe("Implementation plan in markdown"),
7893
+ ordinal: z13.number().optional().describe("Step/order number (0-based)"),
7894
+ storyPointValue: z13.number().optional().describe(SP_DESCRIPTION3)
7895
+ },
7896
+ async ({ parent_task_id, title, description, plan, ordinal, storyPointValue }) => {
7897
+ try {
7898
+ const result = await connection.call(
7899
+ "createProjectSubtask",
7900
+ withRequestingUser(
7901
+ {
7902
+ projectId,
7903
+ parentTaskId: parent_task_id,
7904
+ title,
7905
+ ...description !== void 0 && { description },
7906
+ ...plan !== void 0 && { plan },
7907
+ ...ordinal !== void 0 && { ordinal },
7908
+ ...storyPointValue !== void 0 && { storyPointValue }
7909
+ },
7910
+ getRequestingUserId
7911
+ )
7912
+ );
7913
+ return textResult(`Subtask created: ${result.slug} (ID: ${result.id})`);
7914
+ } catch (error) {
7915
+ return textResult(`Failed to create subtask: ${errorMessage(error)}`);
7916
+ }
7917
+ }
7918
+ );
7919
+ }
7920
+ function buildUpdateSubtaskTool2(connection, getRequestingUserId) {
7921
+ const projectId = connection.projectId;
7922
+ return defineTool(
7923
+ "update_subtask",
7924
+ "Update an existing subtask's fields (title, description, plan, status, ordinal, storyPointValue).",
7925
+ {
7926
+ subtask_id: z13.string().describe("The subtask ID to update"),
7927
+ title: z13.string().optional(),
7928
+ description: z13.string().optional(),
7929
+ plan: z13.string().optional(),
7930
+ status: z13.string().optional(),
7931
+ ordinal: z13.number().optional(),
7932
+ storyPointValue: z13.number().optional().describe(SP_DESCRIPTION3)
7933
+ },
7934
+ async ({ subtask_id, ...fields }) => {
7935
+ try {
7936
+ await connection.call(
7937
+ "updateProjectSubtask",
7938
+ withRequestingUser({ projectId, subtaskId: subtask_id, ...fields }, getRequestingUserId)
7939
+ );
7940
+ return textResult("Subtask updated.");
7941
+ } catch (error) {
7942
+ return textResult(`Failed: ${errorMessage(error)}`);
7943
+ }
7944
+ }
7945
+ );
7946
+ }
7947
+ function buildDeleteSubtaskTool2(connection, getRequestingUserId) {
7948
+ const projectId = connection.projectId;
7949
+ return defineTool(
7950
+ "delete_subtask",
7951
+ "Delete a subtask by id. Use when a subtask was created in error or is no longer needed.",
7952
+ { subtask_id: z13.string().describe("The subtask ID to delete") },
7953
+ async ({ subtask_id }) => {
7954
+ try {
7955
+ await connection.call(
7956
+ "deleteProjectSubtask",
7957
+ withRequestingUser({ projectId, subtaskId: subtask_id }, getRequestingUserId)
7958
+ );
7959
+ return textResult("Subtask deleted.");
7960
+ } catch (error) {
7961
+ return textResult(`Failed: ${errorMessage(error)}`);
7962
+ }
7963
+ }
7964
+ );
7965
+ }
7966
+ function buildStartTaskTool(connection, getRequestingUserId) {
7967
+ const projectId = connection.projectId;
7968
+ return defineTool(
7969
+ "start_task",
7970
+ "Start a cloud build (codespace) for a task. Preconditions: task has a story point value and an agent assigned.",
7971
+ { task_id: z13.string().describe("The task ID to start a build for") },
7972
+ async ({ task_id }) => {
7973
+ try {
7974
+ const result = await connection.call(
7975
+ "startProjectBuild",
7976
+ withRequestingUser({ projectId, taskId: task_id }, getRequestingUserId)
7977
+ );
7978
+ return textResult(
7979
+ `Cloud build started for task ${result.taskId} (status: ${result.status})`
7980
+ );
7981
+ } catch (error) {
7982
+ return textResult(`Failed to start task: ${errorMessage(error)}`);
7983
+ }
7984
+ }
7985
+ );
7986
+ }
7987
+ function buildStopTaskTool(connection, getRequestingUserId) {
7988
+ const projectId = connection.projectId;
7989
+ return defineTool(
7990
+ "stop_task",
7991
+ "Send a stop signal to a running task's cloud build. Not a force-kill \u2014 the agent may take a moment to wind down.",
7992
+ { task_id: z13.string().describe("The task ID whose build should be stopped") },
7993
+ async ({ task_id }) => {
7994
+ try {
7995
+ await connection.call(
7996
+ "stopProjectBuild",
7997
+ withRequestingUser({ projectId, taskId: task_id }, getRequestingUserId)
7998
+ );
7999
+ return textResult(`Stop signal sent for task ${task_id}.`);
8000
+ } catch (error) {
8001
+ return textResult(`Failed to stop task: ${errorMessage(error)}`);
8002
+ }
8003
+ }
8004
+ );
8005
+ }
8006
+ function buildMergePRTool(connection, getRequestingUserId) {
8007
+ const projectId = connection.projectId;
8008
+ return defineTool(
8009
+ "merge_pr",
8010
+ "Approve and merge a task's PR. Preconditions: task in ReviewPR with an open PR. Returns { merged }: false means automerge queued \u2014 wait for status to flip to ReviewDev.",
8011
+ { task_id: z13.string().describe("The task ID whose PR should be approved and merged") },
8012
+ async ({ task_id }) => {
8013
+ try {
8014
+ const result = await connection.call(
8015
+ "approveProjectMergePR",
8016
+ withRequestingUser({ projectId, childTaskId: task_id }, getRequestingUserId)
8017
+ );
8018
+ if (result.merged) {
7686
8019
  return textResult(
7687
- `Failed to get task: ${error instanceof Error ? error.message : "Unknown error"}`
8020
+ `PR #${result.prNumber} approved and merged for task ${result.childTaskId}.`
7688
8021
  );
7689
8022
  }
7690
- },
7691
- { annotations: { readOnlyHint: true } }
7692
- ),
7693
- defineTool(
7694
- "search_tasks",
7695
- "Search tasks by tags, text query, or status filters.",
7696
- {
7697
- tagNames: z13.array(z13.string()).optional().describe("Filter by tag names"),
7698
- searchQuery: z13.string().optional().describe("Text search in title/description"),
7699
- statusFilters: z13.array(z13.string()).optional().describe("Filter by statuses"),
7700
- limit: z13.number().optional().describe("Max results (default 20)")
7701
- },
7702
- async (params) => {
7703
- try {
7704
- const tasks = await connection.call("searchProjectTasks", { projectId, ...params });
7705
- return textResult(JSON.stringify(tasks, null, 2));
7706
- } catch (error) {
8023
+ return textResult(
8024
+ `PR #${result.prNumber} merge queued for task ${result.childTaskId}. The PR will auto-merge when checks pass \u2014 wait for status to flip to ReviewDev.`
8025
+ );
8026
+ } catch (error) {
8027
+ return textResult(`Failed to merge PR: ${errorMessage(error)}`);
8028
+ }
8029
+ }
8030
+ );
8031
+ }
8032
+ function buildCreateSuggestionTool2(connection, getRequestingUserId) {
8033
+ const projectId = connection.projectId;
8034
+ return defineTool(
8035
+ "create_suggestion",
8036
+ "Suggest a feature, improvement, rule, or idea for the project. Duplicates are deduped and your upvote is recorded.",
8037
+ {
8038
+ title: z13.string().describe("Short title for the suggestion"),
8039
+ description: z13.string().optional().describe("1-2 sentence description of what should change and why."),
8040
+ tag_names: z13.array(z13.string()).optional().describe("Tag names to categorize the suggestion")
8041
+ },
8042
+ async ({ title, description, tag_names }) => {
8043
+ try {
8044
+ const result = await connection.call(
8045
+ "createProjectSuggestion",
8046
+ withRequestingUser(
8047
+ {
8048
+ projectId,
8049
+ title,
8050
+ ...description !== void 0 && { description },
8051
+ ...tag_names !== void 0 && { tagNames: tag_names }
8052
+ },
8053
+ getRequestingUserId
8054
+ )
8055
+ );
8056
+ if (result.merged) {
7707
8057
  return textResult(
7708
- `Failed to search tasks: ${error instanceof Error ? error.message : "Unknown error"}`
8058
+ `Your suggestion was merged into an existing one (ID: ${result.mergedIntoId ?? result.id}). Your upvote has been recorded.`
7709
8059
  );
7710
8060
  }
7711
- },
7712
- { annotations: { readOnlyHint: true } }
7713
- )
7714
- ];
8061
+ return textResult(`Suggestion created (ID: ${result.id}).`);
8062
+ } catch (error) {
8063
+ return textResult(`Failed to create suggestion: ${errorMessage(error)}`);
8064
+ }
8065
+ }
8066
+ );
8067
+ }
8068
+ function buildVoteSuggestionTool2(connection, getRequestingUserId) {
8069
+ const projectId = connection.projectId;
8070
+ return defineTool(
8071
+ "vote_suggestion",
8072
+ "Vote +1 or -1 on a project suggestion.",
8073
+ {
8074
+ suggestion_id: z13.string().describe("The suggestion ID to vote on"),
8075
+ value: z13.number().refine((v) => v === 1 || v === -1, { message: "Value must be 1 or -1" }).describe("+1 to upvote, -1 to downvote")
8076
+ },
8077
+ async ({ suggestion_id, value }) => {
8078
+ try {
8079
+ const result = await connection.call(
8080
+ "voteProjectSuggestion",
8081
+ withRequestingUser(
8082
+ {
8083
+ projectId,
8084
+ suggestionId: suggestion_id,
8085
+ value
8086
+ },
8087
+ getRequestingUserId
8088
+ )
8089
+ );
8090
+ return textResult(`Vote recorded. Current score: ${result.score}`);
8091
+ } catch (error) {
8092
+ return textResult(`Failed to vote: ${errorMessage(error)}`);
8093
+ }
8094
+ }
8095
+ );
8096
+ }
8097
+ function buildAddDependencyTool2(connection, getRequestingUserId) {
8098
+ const projectId = connection.projectId;
8099
+ return defineTool(
8100
+ "add_dependency",
8101
+ "Add a blocking dependency \u2014 a task cannot start until the named task is merged to dev.",
8102
+ {
8103
+ task_id: z13.string().describe("The task that should be blocked"),
8104
+ depends_on_slug_or_id: z13.string().describe("Slug or ID of the task this task depends on")
8105
+ },
8106
+ async ({ task_id, depends_on_slug_or_id }) => {
8107
+ try {
8108
+ await connection.call(
8109
+ "addProjectTaskDependency",
8110
+ withRequestingUser(
8111
+ {
8112
+ projectId,
8113
+ taskId: task_id,
8114
+ dependsOnSlugOrId: depends_on_slug_or_id
8115
+ },
8116
+ getRequestingUserId
8117
+ )
8118
+ );
8119
+ return textResult(
8120
+ `Dependency added: task ${task_id} now depends on "${depends_on_slug_or_id}"`
8121
+ );
8122
+ } catch (error) {
8123
+ return textResult(`Failed to add dependency: ${errorMessage(error)}`);
8124
+ }
8125
+ }
8126
+ );
7715
8127
  }
7716
- function buildProjectInfoTools(connection) {
8128
+ function buildRemoveDependencyTool2(connection, getRequestingUserId) {
7717
8129
  const projectId = connection.projectId;
8130
+ return defineTool(
8131
+ "remove_dependency",
8132
+ "Remove a previously added dependency from a task.",
8133
+ {
8134
+ task_id: z13.string().describe("The task to update"),
8135
+ depends_on_slug_or_id: z13.string().describe("Slug or ID of the task to remove as dependency")
8136
+ },
8137
+ async ({ task_id, depends_on_slug_or_id }) => {
8138
+ try {
8139
+ await connection.call(
8140
+ "removeProjectTaskDependency",
8141
+ withRequestingUser(
8142
+ {
8143
+ projectId,
8144
+ taskId: task_id,
8145
+ dependsOnSlugOrId: depends_on_slug_or_id
8146
+ },
8147
+ getRequestingUserId
8148
+ )
8149
+ );
8150
+ return textResult("Dependency removed");
8151
+ } catch (error) {
8152
+ return textResult(`Failed to remove dependency: ${errorMessage(error)}`);
8153
+ }
8154
+ }
8155
+ );
8156
+ }
8157
+ function buildProjectActionTools(connection, getRequestingUserId = () => void 0) {
7718
8158
  return [
7719
- defineTool(
7720
- "list_tags",
7721
- "List all tags available in the project.",
7722
- {},
7723
- async () => {
7724
- try {
7725
- const tags = await connection.call("listProjectTags", { projectId });
7726
- return textResult(JSON.stringify(tags, null, 2));
7727
- } catch (error) {
7728
- return textResult(
7729
- `Failed to list tags: ${error instanceof Error ? error.message : "Unknown error"}`
7730
- );
7731
- }
7732
- },
7733
- { annotations: { readOnlyHint: true } }
7734
- ),
7735
- defineTool(
7736
- "get_project_summary",
7737
- "Get a summary of the project including task counts by status and active builds.",
7738
- {},
7739
- async () => {
7740
- try {
7741
- const summary = await connection.call("getProjectSummary", { projectId });
7742
- return textResult(JSON.stringify(summary, null, 2));
7743
- } catch (error) {
7744
- return textResult(
7745
- `Failed to get project summary: ${error instanceof Error ? error.message : "Unknown error"}`
7746
- );
7747
- }
7748
- },
7749
- { annotations: { readOnlyHint: true } }
7750
- )
8159
+ buildCreateTaskTool(connection, getRequestingUserId),
8160
+ buildUpdateProjectTaskTool(connection, getRequestingUserId),
8161
+ buildUpdateTaskPlanTool(connection, getRequestingUserId),
8162
+ buildForceUpdateStatusTool(connection, getRequestingUserId),
8163
+ buildCreateSubtaskTool2(connection, getRequestingUserId),
8164
+ buildUpdateSubtaskTool2(connection, getRequestingUserId),
8165
+ buildDeleteSubtaskTool2(connection, getRequestingUserId),
8166
+ buildStartTaskTool(connection, getRequestingUserId),
8167
+ buildStopTaskTool(connection, getRequestingUserId),
8168
+ buildMergePRTool(connection, getRequestingUserId),
8169
+ buildCreateSuggestionTool2(connection, getRequestingUserId),
8170
+ buildVoteSuggestionTool2(connection, getRequestingUserId),
8171
+ buildAddDependencyTool2(connection, getRequestingUserId),
8172
+ buildRemoveDependencyTool2(connection, getRequestingUserId)
7751
8173
  ];
7752
8174
  }
7753
- function buildMutationTools2(connection) {
8175
+
8176
+ // src/tools/project-tools.ts
8177
+ function errorMessage(error) {
8178
+ return error instanceof Error ? error.message : "Unknown error";
8179
+ }
8180
+ function buildListTasksTool(connection) {
7754
8181
  const projectId = connection.projectId;
7755
- return [
7756
- defineTool(
7757
- "create_task",
7758
- "Create a new task in the project.",
7759
- {
7760
- title: z13.string().describe("Task title"),
7761
- description: z13.string().optional().describe("Task description"),
7762
- plan: z13.string().optional().describe("Implementation plan in markdown"),
7763
- status: z13.string().optional().describe("Initial status (default: Planning)")
7764
- },
7765
- async (params) => {
7766
- try {
7767
- const result = await connection.call("createProjectTask", { projectId, ...params });
7768
- return textResult(`Task created: ${result.slug} (ID: ${result.id})`);
7769
- } catch (error) {
7770
- return textResult(
7771
- `Failed to create task: ${error instanceof Error ? error.message : "Unknown error"}`
7772
- );
7773
- }
8182
+ return defineTool(
8183
+ "list_tasks",
8184
+ "List tasks in the project. Optionally filter by status or assignee.",
8185
+ {
8186
+ status: z14.string().optional().describe("Filter by task status"),
8187
+ assigneeId: z14.string().optional().describe("Filter by assigned user ID"),
8188
+ limit: z14.number().optional().describe("Max number of tasks to return (default 50)")
8189
+ },
8190
+ async (params) => {
8191
+ try {
8192
+ const tasks = await connection.call("listProjectTasks", { projectId, ...params });
8193
+ return textResult(JSON.stringify(tasks, null, 2));
8194
+ } catch (error) {
8195
+ return textResult(`Failed to list tasks: ${errorMessage(error)}`);
7774
8196
  }
7775
- ),
7776
- defineTool(
7777
- "update_project_task",
7778
- "Update an existing task's title, description, plan, status, or assignee. Project-runner scope.",
7779
- {
7780
- task_id: z13.string().describe("The task ID to update"),
7781
- title: z13.string().optional().describe("New title"),
7782
- description: z13.string().optional().describe("New description"),
7783
- plan: z13.string().optional().describe("New plan in markdown"),
7784
- status: z13.string().optional().describe("New status"),
7785
- assignedUserId: z13.string().nullable().optional().describe("Assign to user ID, or null to unassign")
7786
- },
7787
- async ({ task_id, ...fields }) => {
7788
- try {
7789
- await connection.call("updateProjectTask", { projectId, taskId: task_id, ...fields });
7790
- return textResult("Task updated successfully.");
7791
- } catch (error) {
7792
- return textResult(
7793
- `Failed to update task: ${error instanceof Error ? error.message : "Unknown error"}`
7794
- );
8197
+ },
8198
+ { annotations: { readOnlyHint: true } }
8199
+ );
8200
+ }
8201
+ function buildGetProjectTaskTool(connection) {
8202
+ const projectId = connection.projectId;
8203
+ return defineTool(
8204
+ "get_project_task",
8205
+ "Get detailed information about a task in this project (chat messages, child tasks, session). Project-runner scope.",
8206
+ { task_id: z14.string().describe("The task ID to look up") },
8207
+ async ({ task_id }) => {
8208
+ try {
8209
+ const task = await connection.call("getProjectTask", { projectId, taskId: task_id });
8210
+ return textResult(JSON.stringify(task, null, 2));
8211
+ } catch (error) {
8212
+ return textResult(`Failed to get task: ${errorMessage(error)}`);
8213
+ }
8214
+ },
8215
+ { annotations: { readOnlyHint: true } }
8216
+ );
8217
+ }
8218
+ function buildSearchTasksTool(connection) {
8219
+ const projectId = connection.projectId;
8220
+ return defineTool(
8221
+ "search_tasks",
8222
+ "Search tasks by tags, text query, or status filters.",
8223
+ {
8224
+ tagNames: z14.array(z14.string()).optional().describe("Filter by tag names"),
8225
+ searchQuery: z14.string().optional().describe("Text search in title/description"),
8226
+ statusFilters: z14.array(z14.string()).optional().describe("Filter by statuses"),
8227
+ limit: z14.number().optional().describe("Max results (default 20)")
8228
+ },
8229
+ async (params) => {
8230
+ try {
8231
+ const tasks = await connection.call("searchProjectTasks", { projectId, ...params });
8232
+ return textResult(JSON.stringify(tasks, null, 2));
8233
+ } catch (error) {
8234
+ return textResult(`Failed to search tasks: ${errorMessage(error)}`);
8235
+ }
8236
+ },
8237
+ { annotations: { readOnlyHint: true } }
8238
+ );
8239
+ }
8240
+ function buildListSubtasksTool2(connection) {
8241
+ const projectId = connection.projectId;
8242
+ return defineTool(
8243
+ "list_subtasks",
8244
+ "List the immediate child tasks under a parent task. Use to coordinate subtask work \u2014 see status, ordering, and PR state.",
8245
+ { task_id: z14.string().describe("Parent task ID") },
8246
+ async ({ task_id }) => {
8247
+ try {
8248
+ const subtasks = await connection.call("listProjectSubtasks", {
8249
+ projectId,
8250
+ taskId: task_id
8251
+ });
8252
+ return textResult(JSON.stringify(subtasks, null, 2));
8253
+ } catch (error) {
8254
+ return textResult(`Failed to list subtasks: ${errorMessage(error)}`);
8255
+ }
8256
+ },
8257
+ { annotations: { readOnlyHint: true } }
8258
+ );
8259
+ }
8260
+ function buildListTagsTool(connection) {
8261
+ const projectId = connection.projectId;
8262
+ return defineTool(
8263
+ "list_tags",
8264
+ "List all tags available in the project.",
8265
+ {},
8266
+ async () => {
8267
+ try {
8268
+ const tags = await connection.call("listProjectTags", { projectId });
8269
+ return textResult(JSON.stringify(tags, null, 2));
8270
+ } catch (error) {
8271
+ return textResult(`Failed to list tags: ${errorMessage(error)}`);
8272
+ }
8273
+ },
8274
+ { annotations: { readOnlyHint: true } }
8275
+ );
8276
+ }
8277
+ function buildGetProjectSummaryTool(connection) {
8278
+ const projectId = connection.projectId;
8279
+ return defineTool(
8280
+ "get_project_summary",
8281
+ "Get a summary of the project including task counts by status and active builds.",
8282
+ {},
8283
+ async () => {
8284
+ try {
8285
+ const summary = await connection.call("getProjectSummary", { projectId });
8286
+ return textResult(JSON.stringify(summary, null, 2));
8287
+ } catch (error) {
8288
+ return textResult(`Failed to get project summary: ${errorMessage(error)}`);
8289
+ }
8290
+ },
8291
+ { annotations: { readOnlyHint: true } }
8292
+ );
8293
+ }
8294
+ function buildPostToChatTool2(connection, getRequestingUserId) {
8295
+ const projectId = connection.projectId;
8296
+ return defineTool(
8297
+ "post_to_chat",
8298
+ "Post an out-of-band message into a chat. Omit task_id to post into the project chat; pass task_id to post into a specific task's chat. Normal replies already appear in chat automatically.",
8299
+ {
8300
+ message: z14.string().describe("The message content to post"),
8301
+ task_id: z14.string().optional().describe("Task ID to post into a specific task's chat. Omit for the project chat.")
8302
+ },
8303
+ async ({ message, task_id }) => {
8304
+ try {
8305
+ const requestingUserId = getRequestingUserId();
8306
+ if (task_id) {
8307
+ await connection.call("postToProjectTaskChat", {
8308
+ projectId,
8309
+ taskId: task_id,
8310
+ content: message,
8311
+ ...requestingUserId ? { requestingUserId } : {}
8312
+ });
8313
+ return textResult(JSON.stringify({ posted: true, target: `task:${task_id}` }));
7795
8314
  }
8315
+ await connection.call("postProjectAgentMessage", { projectId, content: message });
8316
+ return textResult(JSON.stringify({ posted: true, target: "project" }));
8317
+ } catch (error) {
8318
+ return textResult(`Failed to post message: ${errorMessage(error)}`);
7796
8319
  }
7797
- )
7798
- ];
8320
+ }
8321
+ );
8322
+ }
8323
+ function buildReadChatHistoryTool(connection) {
8324
+ const projectId = connection.projectId;
8325
+ return defineTool(
8326
+ "read_chat_history",
8327
+ "Read recent messages from a chat. Omit chat_id to read the project chat; pass a chat_id to read a specific chat.",
8328
+ {
8329
+ chat_id: z14.string().optional().describe("Chat ID to read. Omit for the project chat."),
8330
+ limit: z14.number().optional().describe("Number of recent messages to fetch (default 50)")
8331
+ },
8332
+ async ({ chat_id, limit }) => {
8333
+ try {
8334
+ const messages = await connection.call("getProjectChatHistory", {
8335
+ projectId,
8336
+ ...chat_id !== void 0 && { chatId: chat_id },
8337
+ ...limit !== void 0 && { limit }
8338
+ });
8339
+ return textResult(JSON.stringify(messages, null, 2));
8340
+ } catch (error) {
8341
+ return textResult(`Failed to read chat history: ${errorMessage(error)}`);
8342
+ }
8343
+ },
8344
+ { annotations: { readOnlyHint: true } }
8345
+ );
7799
8346
  }
7800
- function buildProjectTools(connection) {
8347
+ function buildReadTaskChatTool2(connection) {
8348
+ const projectId = connection.projectId;
8349
+ return defineTool(
8350
+ "read_task_chat",
8351
+ "Read recent human/user chat messages for a specific task in this project. For agent execution logs use get_task_logs.",
8352
+ {
8353
+ task_id: z14.string().describe("The task ID whose chat to read"),
8354
+ limit: z14.number().optional().describe("Number of recent messages to fetch (default 20)")
8355
+ },
8356
+ async ({ task_id, limit }) => {
8357
+ try {
8358
+ const messages = await connection.call("getProjectTaskChat", {
8359
+ projectId,
8360
+ taskId: task_id,
8361
+ ...limit !== void 0 && { limit }
8362
+ });
8363
+ return textResult(JSON.stringify(messages, null, 2));
8364
+ } catch (error) {
8365
+ return textResult(`Failed to read task chat: ${errorMessage(error)}`);
8366
+ }
8367
+ },
8368
+ { annotations: { readOnlyHint: true } }
8369
+ );
8370
+ }
8371
+ function buildGetTaskLogsTool(connection) {
8372
+ const projectId = connection.projectId;
8373
+ return defineTool(
8374
+ "get_task_logs",
8375
+ "Read CLI execution logs for a task \u2014 agent reasoning, tool calls, and setup/dev-server output. For human chat use read_task_chat.",
8376
+ {
8377
+ task_id: z14.string().describe("Task ID to read logs from"),
8378
+ source: z14.enum(["agent", "application"]).optional().describe("Filter by log source. Omit for all logs."),
8379
+ limit: z14.number().optional().describe("Max number of log entries to return (default 50, max 500).")
8380
+ },
8381
+ async ({ task_id, source, limit }) => {
8382
+ try {
8383
+ const logs = await connection.call("getProjectTaskCli", {
8384
+ projectId,
8385
+ taskId: task_id,
8386
+ ...source !== void 0 && { source },
8387
+ ...limit !== void 0 && { limit }
8388
+ });
8389
+ return textResult(JSON.stringify(logs, null, 2));
8390
+ } catch (error) {
8391
+ return textResult(`Failed to fetch task logs: ${errorMessage(error)}`);
8392
+ }
8393
+ },
8394
+ { annotations: { readOnlyHint: true } }
8395
+ );
8396
+ }
8397
+ function buildProjectTools(connection, getRequestingUserId = () => void 0) {
7801
8398
  return [
7802
- ...buildTaskListTools(connection),
7803
- ...buildProjectInfoTools(connection),
7804
- ...buildMutationTools2(connection)
8399
+ buildListTasksTool(connection),
8400
+ buildGetProjectTaskTool(connection),
8401
+ buildSearchTasksTool(connection),
8402
+ buildListSubtasksTool2(connection),
8403
+ buildListTagsTool(connection),
8404
+ buildGetProjectSummaryTool(connection),
8405
+ buildPostToChatTool2(connection, getRequestingUserId),
8406
+ buildReadChatHistoryTool(connection),
8407
+ buildReadTaskChatTool2(connection),
8408
+ buildGetTaskLogsTool(connection),
8409
+ ...buildProjectActionTools(connection, getRequestingUserId)
7805
8410
  ];
7806
8411
  }
7807
8412
 
@@ -7867,13 +8472,13 @@ async function fetchContext(connection, chatId) {
7867
8472
  }
7868
8473
  return { agentCtx, chatHistory };
7869
8474
  }
7870
- function buildChatQueryOptions(agentCtx, projectDir, connection) {
8475
+ function buildChatQueryOptions(agentCtx, projectDir, connection, getRequestingUserId) {
7871
8476
  const model = agentCtx?.model || FALLBACK_MODEL;
7872
8477
  const settings = agentCtx?.agentSettings ?? {};
7873
8478
  const harness = createHarness();
7874
8479
  const mcpServer = harness.createMcpServer({
7875
8480
  name: "conveyor",
7876
- tools: buildProjectTools(connection)
8481
+ tools: buildProjectTools(connection, getRequestingUserId)
7877
8482
  });
7878
8483
  return {
7879
8484
  options: {
@@ -7942,7 +8547,13 @@ function emitResultCost(event, connection) {
7942
8547
  }
7943
8548
  async function runChatQuery(message, connection, projectDir, sessionId) {
7944
8549
  const { agentCtx, chatHistory } = await fetchContext(connection, message.chatId);
7945
- const { options, harness } = buildChatQueryOptions(agentCtx, projectDir, connection);
8550
+ const requestingUserId = message.userId;
8551
+ const { options, harness } = buildChatQueryOptions(
8552
+ agentCtx,
8553
+ projectDir,
8554
+ connection,
8555
+ () => requestingUserId
8556
+ );
7946
8557
  const prompt = buildPrompt(message, chatHistory);
7947
8558
  connection.emitStatus("busy");
7948
8559
  const events = harness.executeQuery({
@@ -8003,15 +8614,15 @@ function parseErrorMessage(error) {
8003
8614
 
8004
8615
  // src/runner/project-runner-children.ts
8005
8616
  import { fork } from "child_process";
8006
- import { execSync as execSync7 } from "child_process";
8617
+ import { execSync as execSync8 } from "child_process";
8007
8618
  import * as path from "path";
8008
8619
  import { fileURLToPath as fileURLToPath2 } from "url";
8009
8620
 
8010
8621
  // src/runner/project-runner-git.ts
8011
- import { execSync as execSync6 } from "child_process";
8622
+ import { execSync as execSync7 } from "child_process";
8012
8623
 
8013
8624
  // src/runner/project-runner-sync.ts
8014
- import { execSync as execSync5 } from "child_process";
8625
+ import { execSync as execSync6 } from "child_process";
8015
8626
  var logger7 = createServiceLogger("ProjectRunner");
8016
8627
  var START_CMD_KILL_TIMEOUT_MS = 5e3;
8017
8628
  async function executeSetupCommand(projectDir, connection) {
@@ -8108,7 +8719,7 @@ async function handleSyncEnvironment(projectDir, branchSwitchCommand, state, con
8108
8719
  }
8109
8720
  function getChangedFiles(projectDir, previousSha, newSha) {
8110
8721
  try {
8111
- return execSync5(`git diff --name-only ${previousSha}..${newSha}`, {
8722
+ return execSync6(`git diff --name-only ${previousSha}..${newSha}`, {
8112
8723
  cwd: projectDir,
8113
8724
  stdio: ["ignore", "pipe", "ignore"]
8114
8725
  }).toString().trim().split("\n").filter(Boolean);
@@ -8119,7 +8730,7 @@ function getChangedFiles(projectDir, previousSha, newSha) {
8119
8730
  function runIndividualSyncSteps(projectDir, needsInstall, needsPrisma, stepsRun) {
8120
8731
  if (needsInstall) {
8121
8732
  try {
8122
- execSync5("bun install", { cwd: projectDir, timeout: 12e4, stdio: "pipe" });
8733
+ execSync6("bun install", { cwd: projectDir, timeout: 12e4, stdio: "pipe" });
8123
8734
  stepsRun.push("install");
8124
8735
  } catch (err) {
8125
8736
  const msg = parseErrorMessage(err);
@@ -8128,8 +8739,8 @@ function runIndividualSyncSteps(projectDir, needsInstall, needsPrisma, stepsRun)
8128
8739
  }
8129
8740
  if (needsPrisma) {
8130
8741
  try {
8131
- execSync5("bunx prisma generate", { cwd: projectDir, timeout: 6e4, stdio: "pipe" });
8132
- execSync5("bunx prisma db push --accept-data-loss", {
8742
+ execSync6("bunx prisma generate", { cwd: projectDir, timeout: 6e4, stdio: "pipe" });
8743
+ execSync6("bunx prisma db push --accept-data-loss", {
8133
8744
  cwd: projectDir,
8134
8745
  timeout: 6e4,
8135
8746
  stdio: "pipe"
@@ -8173,7 +8784,7 @@ async function smartSync(projectDir, branchSwitchCommand, state, connection, pre
8173
8784
  }
8174
8785
  await killStartCommand(state);
8175
8786
  try {
8176
- execSync5(`git pull origin ${branch}`, {
8787
+ execSync6(`git pull origin ${branch}`, {
8177
8788
  cwd: projectDir,
8178
8789
  stdio: "pipe",
8179
8790
  timeout: 6e4
@@ -8209,10 +8820,10 @@ function setupWorkDir(projectDir, assignment) {
8209
8820
  logger8.warn("Uncommitted changes, skipping checkout", { taskId: shortId, branch });
8210
8821
  } else {
8211
8822
  try {
8212
- execSync6(`git checkout ${branch}`, { cwd: workDir, stdio: "ignore" });
8823
+ execSync7(`git checkout ${branch}`, { cwd: workDir, stdio: "ignore" });
8213
8824
  } catch {
8214
8825
  try {
8215
- execSync6(`git checkout -b ${branch}`, { cwd: workDir, stdio: "ignore" });
8826
+ execSync7(`git checkout -b ${branch}`, { cwd: workDir, stdio: "ignore" });
8216
8827
  } catch {
8217
8828
  logger8.warn("Could not checkout branch", { taskId: shortId, branch });
8218
8829
  }
@@ -8231,8 +8842,8 @@ function checkoutWorkspaceBranch(projectDir) {
8231
8842
  return;
8232
8843
  }
8233
8844
  try {
8234
- execSync6(`git fetch origin ${workspaceBranch}`, { cwd: projectDir, stdio: "pipe" });
8235
- execSync6(`git checkout ${workspaceBranch}`, { cwd: projectDir, stdio: "pipe" });
8845
+ execSync7(`git fetch origin ${workspaceBranch}`, { cwd: projectDir, stdio: "pipe" });
8846
+ execSync7(`git checkout ${workspaceBranch}`, { cwd: projectDir, stdio: "pipe" });
8236
8847
  logger8.info("Checked out workspace branch", { workspaceBranch });
8237
8848
  } catch {
8238
8849
  logger8.warn("Failed to checkout workspace branch", { workspaceBranch });
@@ -8241,20 +8852,20 @@ function checkoutWorkspaceBranch(projectDir) {
8241
8852
  async function handleSwitchBranch(projectDir, branchSwitchCommand, startCmd, connection, commitWatcher, data, callback) {
8242
8853
  try {
8243
8854
  try {
8244
- execSync6("git fetch origin", { cwd: projectDir, stdio: "pipe" });
8855
+ execSync7("git fetch origin", { cwd: projectDir, stdio: "pipe" });
8245
8856
  } catch {
8246
8857
  logger8.warn("Git fetch failed during branch switch");
8247
8858
  }
8248
8859
  detachWorktreeBranch(projectDir, data.branch);
8249
8860
  try {
8250
- execSync6(`git checkout ${data.branch}`, { cwd: projectDir, stdio: "pipe" });
8861
+ execSync7(`git checkout ${data.branch}`, { cwd: projectDir, stdio: "pipe" });
8251
8862
  } catch (err) {
8252
8863
  const msg = parseErrorMessage(err);
8253
8864
  callback({ ok: false, error: `Failed to checkout branch: ${msg}` });
8254
8865
  return;
8255
8866
  }
8256
8867
  try {
8257
- execSync6(`git pull origin ${data.branch}`, { cwd: projectDir, stdio: "pipe" });
8868
+ execSync7(`git pull origin ${data.branch}`, { cwd: projectDir, stdio: "pipe" });
8258
8869
  } catch {
8259
8870
  logger8.warn("Git pull failed during branch switch");
8260
8871
  }
@@ -8397,7 +9008,7 @@ async function handleAssignment(assignment, activeAgents, projectDir, connection
8397
9008
  }
8398
9009
  try {
8399
9010
  try {
8400
- execSync7("git fetch origin", { cwd: projectDir, stdio: "ignore" });
9011
+ execSync8("git fetch origin", { cwd: projectDir, stdio: "ignore" });
8401
9012
  } catch {
8402
9013
  logger9.warn("Git fetch failed", { taskId: shortId });
8403
9014
  }
@@ -8458,6 +9069,10 @@ var ProjectRunner = class {
8458
9069
  stopping = false;
8459
9070
  resolveLifecycle = null;
8460
9071
  chatSessionIds = /* @__PURE__ */ new Map();
9072
+ /** ProjectCodespaceSession id assigned at register time. Used to filter
9073
+ * targeted incoming chat messages so we only react to ones addressed
9074
+ * to this specific runner. */
9075
+ projectSessionId = null;
8461
9076
  startCmd = { child: null, running: false };
8462
9077
  setupComplete = false;
8463
9078
  branchSwitchCommand;
@@ -8497,6 +9112,7 @@ var ProjectRunner = class {
8497
9112
  projectId: this.connection.projectId,
8498
9113
  capabilities: ["task", "pm", "code-review", "audit"]
8499
9114
  });
9115
+ this.projectSessionId = registration.sessionId;
8500
9116
  this.branchSwitchCommand = registration.branchSwitchCommand ?? process.env.CONVEYOR_BRANCH_SWITCH_COMMAND;
8501
9117
  logger10.info("Registered as project agent", { agentName: registration.agentName });
8502
9118
  try {
@@ -8603,6 +9219,9 @@ var ProjectRunner = class {
8603
9219
  );
8604
9220
  this.connection.onShutdown(() => void this.stop());
8605
9221
  this.connection.onChatMessage((msg) => {
9222
+ if (msg.targetSessionId && this.projectSessionId && msg.targetSessionId !== this.projectSessionId) {
9223
+ return;
9224
+ }
8606
9225
  const chatId = msg.chatId ?? "default";
8607
9226
  const existingSessionId = this.chatSessionIds.get(chatId);
8608
9227
  void handleProjectChatMessage(msg, this.connection, this.projectDir, existingSessionId).then(
@@ -8641,10 +9260,11 @@ var ProjectRunner = class {
8641
9260
  }
8642
9261
  async handleReconnect() {
8643
9262
  try {
8644
- await this.connection.call("registerProjectAgent", {
9263
+ const registration = await this.connection.call("registerProjectAgent", {
8645
9264
  projectId: this.connection.projectId,
8646
9265
  capabilities: ["task", "pm", "code-review", "audit"]
8647
9266
  });
9267
+ this.projectSessionId = registration.sessionId;
8648
9268
  this.connection.emitStatus(this.activeAgents.size > 0 ? "busy" : "idle");
8649
9269
  logger10.info("Re-registered after reconnect");
8650
9270
  } catch (error) {
@@ -8804,4 +9424,4 @@ export {
8804
9424
  buildSessionPreviewPorts,
8805
9425
  loadConveyorConfig
8806
9426
  };
8807
- //# sourceMappingURL=chunk-PC43BKMM.js.map
9427
+ //# sourceMappingURL=chunk-Y6IHF32X.js.map