@rallycry/conveyor-agent 7.3.8 → 7.3.10

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -700,6 +700,7 @@ var Lifecycle = class {
700
700
  // ── Periodic git flush ────────────────────────────────────────────
701
701
  startGitFlush() {
702
702
  this.stopGitFlush();
703
+ if (this.config.gitFlushIntervalMs <= 0) return;
703
704
  this.gitFlushTimer = setInterval(() => {
704
705
  this.callbacks.onGitFlush();
705
706
  }, this.config.gitFlushIntervalMs);
@@ -1395,6 +1396,8 @@ var DeleteSubtaskResponseSchema = z.object({
1395
1396
  });
1396
1397
  var RegisterProjectAgentResponseSchema = z.object({
1397
1398
  registered: z.boolean(),
1399
+ /** ProjectCodespaceSession id — used by the runner to filter targeted chat messages. */
1400
+ sessionId: z.string(),
1398
1401
  agentName: z.string(),
1399
1402
  agentInstructions: z.string(),
1400
1403
  model: z.string(),
@@ -1467,7 +1470,8 @@ var CreateProjectTaskRequestSchema = z2.object({
1467
1470
  title: z2.string().min(1),
1468
1471
  description: z2.string().optional(),
1469
1472
  plan: z2.string().optional(),
1470
- status: z2.string().optional()
1473
+ status: z2.string().optional(),
1474
+ requestingUserId: z2.string().optional()
1471
1475
  });
1472
1476
  var UpdateProjectTaskRequestSchema = z2.object({
1473
1477
  projectId: z2.string(),
@@ -1476,7 +1480,8 @@ var UpdateProjectTaskRequestSchema = z2.object({
1476
1480
  description: z2.string().optional(),
1477
1481
  plan: z2.string().optional(),
1478
1482
  status: z2.string().optional(),
1479
- assignedUserId: z2.string().nullish()
1483
+ assignedUserId: z2.string().nullish(),
1484
+ requestingUserId: z2.string().optional()
1480
1485
  });
1481
1486
  var ReportProjectAgentEventRequestSchema = z2.object({
1482
1487
  projectId: z2.string(),
@@ -1539,7 +1544,8 @@ var GetProjectCliHistoryRequestSchema = z2.object({
1539
1544
  var PostToProjectTaskChatRequestSchema = z2.object({
1540
1545
  projectId: z2.string(),
1541
1546
  taskId: z2.string(),
1542
- content: z2.string()
1547
+ content: z2.string(),
1548
+ requestingUserId: z2.string().optional()
1543
1549
  });
1544
1550
  var GetProjectTaskCliRequestSchema = z2.object({
1545
1551
  projectId: z2.string(),
@@ -1549,15 +1555,71 @@ var GetProjectTaskCliRequestSchema = z2.object({
1549
1555
  });
1550
1556
  var StartProjectBuildRequestSchema = z2.object({
1551
1557
  projectId: z2.string(),
1552
- taskId: z2.string()
1558
+ taskId: z2.string(),
1559
+ requestingUserId: z2.string().optional()
1553
1560
  });
1554
1561
  var StopProjectBuildRequestSchema = z2.object({
1555
1562
  projectId: z2.string(),
1556
- taskId: z2.string()
1563
+ taskId: z2.string(),
1564
+ requestingUserId: z2.string().optional()
1557
1565
  });
1558
1566
  var ApproveProjectMergePRRequestSchema = z2.object({
1559
1567
  projectId: z2.string(),
1560
- childTaskId: z2.string()
1568
+ childTaskId: z2.string(),
1569
+ requestingUserId: z2.string().optional()
1570
+ });
1571
+ var ListProjectSubtasksRequestSchema = z2.object({
1572
+ projectId: z2.string(),
1573
+ taskId: z2.string()
1574
+ });
1575
+ var CreateProjectSubtaskRequestSchema = z2.object({
1576
+ projectId: z2.string(),
1577
+ parentTaskId: z2.string(),
1578
+ title: z2.string().min(1),
1579
+ description: z2.string().optional(),
1580
+ plan: z2.string().optional(),
1581
+ ordinal: z2.number().int().nonnegative().optional(),
1582
+ storyPointValue: z2.number().int().positive().optional(),
1583
+ requestingUserId: z2.string().optional()
1584
+ });
1585
+ var UpdateProjectSubtaskRequestSchema = z2.object({
1586
+ projectId: z2.string(),
1587
+ subtaskId: z2.string(),
1588
+ title: z2.string().optional(),
1589
+ description: z2.string().optional(),
1590
+ plan: z2.string().optional(),
1591
+ status: z2.string().optional(),
1592
+ ordinal: z2.number().int().nonnegative().optional(),
1593
+ storyPointValue: z2.number().int().positive().optional(),
1594
+ requestingUserId: z2.string().optional()
1595
+ });
1596
+ var DeleteProjectSubtaskRequestSchema = z2.object({
1597
+ projectId: z2.string(),
1598
+ subtaskId: z2.string(),
1599
+ requestingUserId: z2.string().optional()
1600
+ });
1601
+ var GetProjectTaskChatRequestSchema = z2.object({
1602
+ projectId: z2.string(),
1603
+ taskId: z2.string(),
1604
+ limit: z2.number().int().positive().optional().default(20)
1605
+ });
1606
+ var AddProjectTaskDependencyRequestSchema = z2.object({
1607
+ projectId: z2.string(),
1608
+ taskId: z2.string(),
1609
+ dependsOnSlugOrId: z2.string(),
1610
+ requestingUserId: z2.string().optional()
1611
+ });
1612
+ var RemoveProjectTaskDependencyRequestSchema = z2.object({
1613
+ projectId: z2.string(),
1614
+ taskId: z2.string(),
1615
+ dependsOnSlugOrId: z2.string(),
1616
+ requestingUserId: z2.string().optional()
1617
+ });
1618
+ var VoteProjectSuggestionRequestSchema = z2.object({
1619
+ projectId: z2.string(),
1620
+ suggestionId: z2.string(),
1621
+ value: z2.union([z2.literal(1), z2.literal(-1)]),
1622
+ requestingUserId: z2.string().optional()
1561
1623
  });
1562
1624
  var StartTaskAuditRequestSchema = z3.object({
1563
1625
  projectId: z3.string(),
@@ -1577,7 +1639,8 @@ var CreateProjectSuggestionRequestSchema = z3.object({
1577
1639
  projectId: z3.string(),
1578
1640
  title: z3.string().min(1),
1579
1641
  description: z3.string().optional(),
1580
- tagNames: z3.array(z3.string()).optional()
1642
+ tagNames: z3.array(z3.string()).optional(),
1643
+ requestingUserId: z3.string().optional()
1581
1644
  });
1582
1645
  var ReportTaskAuditBatchCompleteRequestSchema = z3.object({
1583
1646
  projectId: z3.string(),
@@ -7706,158 +7769,645 @@ function runStartCommand(cmd, cwd, onOutput) {
7706
7769
  }
7707
7770
 
7708
7771
  // src/tools/project-tools.ts
7772
+ import { z as z14 } from "zod";
7773
+
7774
+ // src/tools/project-action-tools.ts
7709
7775
  import { z as z13 } from "zod";
7710
- function buildTaskListTools(connection) {
7776
+ var SP_DESCRIPTION3 = "Story point value (1=Common, 2=Magic, 3=Rare, 5=Unique)";
7777
+ function withRequestingUser(payload, getRequestingUserId) {
7778
+ const requestingUserId = getRequestingUserId();
7779
+ return requestingUserId ? { ...payload, requestingUserId } : payload;
7780
+ }
7781
+ function buildCreateTaskTool(connection, getRequestingUserId) {
7711
7782
  const projectId = connection.projectId;
7712
- return [
7713
- defineTool(
7714
- "list_tasks",
7715
- "List tasks in the project. Optionally filter by status or assignee.",
7716
- {
7717
- status: z13.string().optional().describe("Filter by task status"),
7718
- assigneeId: z13.string().optional().describe("Filter by assigned user ID"),
7719
- limit: z13.number().optional().describe("Max number of tasks to return (default 50)")
7720
- },
7721
- async (params) => {
7722
- try {
7723
- const tasks = await connection.call("listProjectTasks", { projectId, ...params });
7724
- return textResult(JSON.stringify(tasks, null, 2));
7725
- } catch (error) {
7726
- return textResult(
7727
- `Failed to list tasks: ${error instanceof Error ? error.message : "Unknown error"}`
7728
- );
7729
- }
7730
- },
7731
- { annotations: { readOnlyHint: true } }
7732
- ),
7733
- defineTool(
7734
- "get_project_task",
7735
- "Get detailed information about a task in this project (chat messages, child tasks, session). Project-runner scope.",
7736
- { task_id: z13.string().describe("The task ID to look up") },
7737
- async ({ task_id }) => {
7738
- try {
7739
- const task = await connection.call("getProjectTask", { projectId, taskId: task_id });
7740
- return textResult(JSON.stringify(task, null, 2));
7741
- } catch (error) {
7783
+ return defineTool(
7784
+ "create_task",
7785
+ "Create a new task in the project.",
7786
+ {
7787
+ title: z13.string().describe("Task title"),
7788
+ description: z13.string().optional().describe("Task description"),
7789
+ plan: z13.string().optional().describe("Implementation plan in markdown"),
7790
+ status: z13.string().optional().describe("Initial status (default: Planning)")
7791
+ },
7792
+ async (params) => {
7793
+ try {
7794
+ const result = await connection.call(
7795
+ "createProjectTask",
7796
+ withRequestingUser({ projectId, ...params }, getRequestingUserId)
7797
+ );
7798
+ return textResult(`Task created: ${result.slug} (ID: ${result.id})`);
7799
+ } catch (error) {
7800
+ return textResult(`Failed to create task: ${errorMessage(error)}`);
7801
+ }
7802
+ }
7803
+ );
7804
+ }
7805
+ function buildUpdateProjectTaskTool(connection, getRequestingUserId) {
7806
+ const projectId = connection.projectId;
7807
+ return defineTool(
7808
+ "update_project_task",
7809
+ "Update an existing task's title, description, plan, status, or assignee. Project-runner scope.",
7810
+ {
7811
+ task_id: z13.string().describe("The task ID to update"),
7812
+ title: z13.string().optional().describe("New title"),
7813
+ description: z13.string().optional().describe("New description"),
7814
+ plan: z13.string().optional().describe("New plan in markdown"),
7815
+ status: z13.string().optional().describe("New status"),
7816
+ assignedUserId: z13.string().nullable().optional().describe("Assign to user ID, or null to unassign")
7817
+ },
7818
+ async ({ task_id, ...fields }) => {
7819
+ try {
7820
+ await connection.call(
7821
+ "updateProjectTask",
7822
+ withRequestingUser({ projectId, taskId: task_id, ...fields }, getRequestingUserId)
7823
+ );
7824
+ return textResult("Task updated successfully.");
7825
+ } catch (error) {
7826
+ return textResult(`Failed to update task: ${errorMessage(error)}`);
7827
+ }
7828
+ }
7829
+ );
7830
+ }
7831
+ function buildUpdateTaskPlanTool(connection, getRequestingUserId) {
7832
+ const projectId = connection.projectId;
7833
+ return defineTool(
7834
+ "update_task_plan",
7835
+ "Save a plan or description to a task. Convenience wrapper around update_project_task for the common Plan-mode case.",
7836
+ {
7837
+ task_id: z13.string().describe("The task ID to update"),
7838
+ plan: z13.string().optional().describe("The task plan in markdown"),
7839
+ description: z13.string().optional().describe("Updated task description")
7840
+ },
7841
+ async ({ task_id, plan, description }) => {
7842
+ try {
7843
+ await connection.call(
7844
+ "updateProjectTask",
7845
+ withRequestingUser(
7846
+ {
7847
+ projectId,
7848
+ taskId: task_id,
7849
+ ...plan !== void 0 && { plan },
7850
+ ...description !== void 0 && { description }
7851
+ },
7852
+ getRequestingUserId
7853
+ )
7854
+ );
7855
+ return textResult("Task updated successfully.");
7856
+ } catch (error) {
7857
+ return textResult(`Failed to update task: ${errorMessage(error)}`);
7858
+ }
7859
+ }
7860
+ );
7861
+ }
7862
+ function buildForceUpdateStatusTool(connection, getRequestingUserId) {
7863
+ const projectId = connection.projectId;
7864
+ return defineTool(
7865
+ "force_update_task_status",
7866
+ "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.",
7867
+ {
7868
+ task_id: z13.string().describe("The task ID to update"),
7869
+ status: z13.enum(["Planning", "Open", "InProgress", "ReviewPR", "ReviewDev", "ReviewLive", "Complete"]).describe("The new status for the task")
7870
+ },
7871
+ async ({ task_id, status }) => {
7872
+ try {
7873
+ await connection.call(
7874
+ "updateProjectTask",
7875
+ withRequestingUser({ projectId, taskId: task_id, status }, getRequestingUserId)
7876
+ );
7877
+ return textResult(`Task status updated to ${status}.`);
7878
+ } catch (error) {
7879
+ return textResult(`Failed to update status: ${errorMessage(error)}`);
7880
+ }
7881
+ }
7882
+ );
7883
+ }
7884
+ function buildCreateSubtaskTool2(connection, getRequestingUserId) {
7885
+ const projectId = connection.projectId;
7886
+ return defineTool(
7887
+ "create_subtask",
7888
+ "Create a subtask under a parent task in this project. Use for breaking parent tasks into smaller pieces during planning.",
7889
+ {
7890
+ parent_task_id: z13.string().describe("The parent task ID"),
7891
+ title: z13.string().describe("Subtask title"),
7892
+ description: z13.string().optional().describe("Brief description"),
7893
+ plan: z13.string().optional().describe("Implementation plan in markdown"),
7894
+ ordinal: z13.number().optional().describe("Step/order number (0-based)"),
7895
+ storyPointValue: z13.number().optional().describe(SP_DESCRIPTION3)
7896
+ },
7897
+ async ({ parent_task_id, title, description, plan, ordinal, storyPointValue }) => {
7898
+ try {
7899
+ const result = await connection.call(
7900
+ "createProjectSubtask",
7901
+ withRequestingUser(
7902
+ {
7903
+ projectId,
7904
+ parentTaskId: parent_task_id,
7905
+ title,
7906
+ ...description !== void 0 && { description },
7907
+ ...plan !== void 0 && { plan },
7908
+ ...ordinal !== void 0 && { ordinal },
7909
+ ...storyPointValue !== void 0 && { storyPointValue }
7910
+ },
7911
+ getRequestingUserId
7912
+ )
7913
+ );
7914
+ return textResult(`Subtask created: ${result.slug} (ID: ${result.id})`);
7915
+ } catch (error) {
7916
+ return textResult(`Failed to create subtask: ${errorMessage(error)}`);
7917
+ }
7918
+ }
7919
+ );
7920
+ }
7921
+ function buildUpdateSubtaskTool2(connection, getRequestingUserId) {
7922
+ const projectId = connection.projectId;
7923
+ return defineTool(
7924
+ "update_subtask",
7925
+ "Update an existing subtask's fields (title, description, plan, status, ordinal, storyPointValue).",
7926
+ {
7927
+ subtask_id: z13.string().describe("The subtask ID to update"),
7928
+ title: z13.string().optional(),
7929
+ description: z13.string().optional(),
7930
+ plan: z13.string().optional(),
7931
+ status: z13.string().optional(),
7932
+ ordinal: z13.number().optional(),
7933
+ storyPointValue: z13.number().optional().describe(SP_DESCRIPTION3)
7934
+ },
7935
+ async ({ subtask_id, ...fields }) => {
7936
+ try {
7937
+ await connection.call(
7938
+ "updateProjectSubtask",
7939
+ withRequestingUser({ projectId, subtaskId: subtask_id, ...fields }, getRequestingUserId)
7940
+ );
7941
+ return textResult("Subtask updated.");
7942
+ } catch (error) {
7943
+ return textResult(`Failed: ${errorMessage(error)}`);
7944
+ }
7945
+ }
7946
+ );
7947
+ }
7948
+ function buildDeleteSubtaskTool2(connection, getRequestingUserId) {
7949
+ const projectId = connection.projectId;
7950
+ return defineTool(
7951
+ "delete_subtask",
7952
+ "Delete a subtask by id. Use when a subtask was created in error or is no longer needed.",
7953
+ { subtask_id: z13.string().describe("The subtask ID to delete") },
7954
+ async ({ subtask_id }) => {
7955
+ try {
7956
+ await connection.call(
7957
+ "deleteProjectSubtask",
7958
+ withRequestingUser({ projectId, subtaskId: subtask_id }, getRequestingUserId)
7959
+ );
7960
+ return textResult("Subtask deleted.");
7961
+ } catch (error) {
7962
+ return textResult(`Failed: ${errorMessage(error)}`);
7963
+ }
7964
+ }
7965
+ );
7966
+ }
7967
+ function buildStartTaskTool(connection, getRequestingUserId) {
7968
+ const projectId = connection.projectId;
7969
+ return defineTool(
7970
+ "start_task",
7971
+ "Start a cloud build (codespace) for a task. Preconditions: task has a story point value and an agent assigned.",
7972
+ { task_id: z13.string().describe("The task ID to start a build for") },
7973
+ async ({ task_id }) => {
7974
+ try {
7975
+ const result = await connection.call(
7976
+ "startProjectBuild",
7977
+ withRequestingUser({ projectId, taskId: task_id }, getRequestingUserId)
7978
+ );
7979
+ return textResult(
7980
+ `Cloud build started for task ${result.taskId} (status: ${result.status})`
7981
+ );
7982
+ } catch (error) {
7983
+ return textResult(`Failed to start task: ${errorMessage(error)}`);
7984
+ }
7985
+ }
7986
+ );
7987
+ }
7988
+ function buildStopTaskTool(connection, getRequestingUserId) {
7989
+ const projectId = connection.projectId;
7990
+ return defineTool(
7991
+ "stop_task",
7992
+ "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.",
7993
+ { task_id: z13.string().describe("The task ID whose build should be stopped") },
7994
+ async ({ task_id }) => {
7995
+ try {
7996
+ await connection.call(
7997
+ "stopProjectBuild",
7998
+ withRequestingUser({ projectId, taskId: task_id }, getRequestingUserId)
7999
+ );
8000
+ return textResult(`Stop signal sent for task ${task_id}.`);
8001
+ } catch (error) {
8002
+ return textResult(`Failed to stop task: ${errorMessage(error)}`);
8003
+ }
8004
+ }
8005
+ );
8006
+ }
8007
+ function buildMergePRTool(connection, getRequestingUserId) {
8008
+ const projectId = connection.projectId;
8009
+ return defineTool(
8010
+ "merge_pr",
8011
+ "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.",
8012
+ { task_id: z13.string().describe("The task ID whose PR should be approved and merged") },
8013
+ async ({ task_id }) => {
8014
+ try {
8015
+ const result = await connection.call(
8016
+ "approveProjectMergePR",
8017
+ withRequestingUser({ projectId, childTaskId: task_id }, getRequestingUserId)
8018
+ );
8019
+ if (result.merged) {
7742
8020
  return textResult(
7743
- `Failed to get task: ${error instanceof Error ? error.message : "Unknown error"}`
8021
+ `PR #${result.prNumber} approved and merged for task ${result.childTaskId}.`
7744
8022
  );
7745
8023
  }
7746
- },
7747
- { annotations: { readOnlyHint: true } }
7748
- ),
7749
- defineTool(
7750
- "search_tasks",
7751
- "Search tasks by tags, text query, or status filters.",
7752
- {
7753
- tagNames: z13.array(z13.string()).optional().describe("Filter by tag names"),
7754
- searchQuery: z13.string().optional().describe("Text search in title/description"),
7755
- statusFilters: z13.array(z13.string()).optional().describe("Filter by statuses"),
7756
- limit: z13.number().optional().describe("Max results (default 20)")
7757
- },
7758
- async (params) => {
7759
- try {
7760
- const tasks = await connection.call("searchProjectTasks", { projectId, ...params });
7761
- return textResult(JSON.stringify(tasks, null, 2));
7762
- } catch (error) {
8024
+ return textResult(
8025
+ `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.`
8026
+ );
8027
+ } catch (error) {
8028
+ return textResult(`Failed to merge PR: ${errorMessage(error)}`);
8029
+ }
8030
+ }
8031
+ );
8032
+ }
8033
+ function buildCreateSuggestionTool2(connection, getRequestingUserId) {
8034
+ const projectId = connection.projectId;
8035
+ return defineTool(
8036
+ "create_suggestion",
8037
+ "Suggest a feature, improvement, rule, or idea for the project. Duplicates are deduped and your upvote is recorded.",
8038
+ {
8039
+ title: z13.string().describe("Short title for the suggestion"),
8040
+ description: z13.string().optional().describe("1-2 sentence description of what should change and why."),
8041
+ tag_names: z13.array(z13.string()).optional().describe("Tag names to categorize the suggestion")
8042
+ },
8043
+ async ({ title, description, tag_names }) => {
8044
+ try {
8045
+ const result = await connection.call(
8046
+ "createProjectSuggestion",
8047
+ withRequestingUser(
8048
+ {
8049
+ projectId,
8050
+ title,
8051
+ ...description !== void 0 && { description },
8052
+ ...tag_names !== void 0 && { tagNames: tag_names }
8053
+ },
8054
+ getRequestingUserId
8055
+ )
8056
+ );
8057
+ if (result.merged) {
7763
8058
  return textResult(
7764
- `Failed to search tasks: ${error instanceof Error ? error.message : "Unknown error"}`
8059
+ `Your suggestion was merged into an existing one (ID: ${result.mergedIntoId ?? result.id}). Your upvote has been recorded.`
7765
8060
  );
7766
8061
  }
7767
- },
7768
- { annotations: { readOnlyHint: true } }
7769
- )
7770
- ];
8062
+ return textResult(`Suggestion created (ID: ${result.id}).`);
8063
+ } catch (error) {
8064
+ return textResult(`Failed to create suggestion: ${errorMessage(error)}`);
8065
+ }
8066
+ }
8067
+ );
7771
8068
  }
7772
- function buildProjectInfoTools(connection) {
8069
+ function buildVoteSuggestionTool2(connection, getRequestingUserId) {
7773
8070
  const projectId = connection.projectId;
8071
+ return defineTool(
8072
+ "vote_suggestion",
8073
+ "Vote +1 or -1 on a project suggestion.",
8074
+ {
8075
+ suggestion_id: z13.string().describe("The suggestion ID to vote on"),
8076
+ value: z13.number().refine((v) => v === 1 || v === -1, { message: "Value must be 1 or -1" }).describe("+1 to upvote, -1 to downvote")
8077
+ },
8078
+ async ({ suggestion_id, value }) => {
8079
+ try {
8080
+ const result = await connection.call(
8081
+ "voteProjectSuggestion",
8082
+ withRequestingUser(
8083
+ {
8084
+ projectId,
8085
+ suggestionId: suggestion_id,
8086
+ value
8087
+ },
8088
+ getRequestingUserId
8089
+ )
8090
+ );
8091
+ return textResult(`Vote recorded. Current score: ${result.score}`);
8092
+ } catch (error) {
8093
+ return textResult(`Failed to vote: ${errorMessage(error)}`);
8094
+ }
8095
+ }
8096
+ );
8097
+ }
8098
+ function buildAddDependencyTool2(connection, getRequestingUserId) {
8099
+ const projectId = connection.projectId;
8100
+ return defineTool(
8101
+ "add_dependency",
8102
+ "Add a blocking dependency \u2014 a task cannot start until the named task is merged to dev.",
8103
+ {
8104
+ task_id: z13.string().describe("The task that should be blocked"),
8105
+ depends_on_slug_or_id: z13.string().describe("Slug or ID of the task this task depends on")
8106
+ },
8107
+ async ({ task_id, depends_on_slug_or_id }) => {
8108
+ try {
8109
+ await connection.call(
8110
+ "addProjectTaskDependency",
8111
+ withRequestingUser(
8112
+ {
8113
+ projectId,
8114
+ taskId: task_id,
8115
+ dependsOnSlugOrId: depends_on_slug_or_id
8116
+ },
8117
+ getRequestingUserId
8118
+ )
8119
+ );
8120
+ return textResult(
8121
+ `Dependency added: task ${task_id} now depends on "${depends_on_slug_or_id}"`
8122
+ );
8123
+ } catch (error) {
8124
+ return textResult(`Failed to add dependency: ${errorMessage(error)}`);
8125
+ }
8126
+ }
8127
+ );
8128
+ }
8129
+ function buildRemoveDependencyTool2(connection, getRequestingUserId) {
8130
+ const projectId = connection.projectId;
8131
+ return defineTool(
8132
+ "remove_dependency",
8133
+ "Remove a previously added dependency from a task.",
8134
+ {
8135
+ task_id: z13.string().describe("The task to update"),
8136
+ depends_on_slug_or_id: z13.string().describe("Slug or ID of the task to remove as dependency")
8137
+ },
8138
+ async ({ task_id, depends_on_slug_or_id }) => {
8139
+ try {
8140
+ await connection.call(
8141
+ "removeProjectTaskDependency",
8142
+ withRequestingUser(
8143
+ {
8144
+ projectId,
8145
+ taskId: task_id,
8146
+ dependsOnSlugOrId: depends_on_slug_or_id
8147
+ },
8148
+ getRequestingUserId
8149
+ )
8150
+ );
8151
+ return textResult("Dependency removed");
8152
+ } catch (error) {
8153
+ return textResult(`Failed to remove dependency: ${errorMessage(error)}`);
8154
+ }
8155
+ }
8156
+ );
8157
+ }
8158
+ function buildProjectActionTools(connection, getRequestingUserId = () => void 0) {
7774
8159
  return [
7775
- defineTool(
7776
- "list_tags",
7777
- "List all tags available in the project.",
7778
- {},
7779
- async () => {
7780
- try {
7781
- const tags = await connection.call("listProjectTags", { projectId });
7782
- return textResult(JSON.stringify(tags, null, 2));
7783
- } catch (error) {
7784
- return textResult(
7785
- `Failed to list tags: ${error instanceof Error ? error.message : "Unknown error"}`
7786
- );
7787
- }
7788
- },
7789
- { annotations: { readOnlyHint: true } }
7790
- ),
7791
- defineTool(
7792
- "get_project_summary",
7793
- "Get a summary of the project including task counts by status and active builds.",
7794
- {},
7795
- async () => {
7796
- try {
7797
- const summary = await connection.call("getProjectSummary", { projectId });
7798
- return textResult(JSON.stringify(summary, null, 2));
7799
- } catch (error) {
7800
- return textResult(
7801
- `Failed to get project summary: ${error instanceof Error ? error.message : "Unknown error"}`
7802
- );
7803
- }
7804
- },
7805
- { annotations: { readOnlyHint: true } }
7806
- )
8160
+ buildCreateTaskTool(connection, getRequestingUserId),
8161
+ buildUpdateProjectTaskTool(connection, getRequestingUserId),
8162
+ buildUpdateTaskPlanTool(connection, getRequestingUserId),
8163
+ buildForceUpdateStatusTool(connection, getRequestingUserId),
8164
+ buildCreateSubtaskTool2(connection, getRequestingUserId),
8165
+ buildUpdateSubtaskTool2(connection, getRequestingUserId),
8166
+ buildDeleteSubtaskTool2(connection, getRequestingUserId),
8167
+ buildStartTaskTool(connection, getRequestingUserId),
8168
+ buildStopTaskTool(connection, getRequestingUserId),
8169
+ buildMergePRTool(connection, getRequestingUserId),
8170
+ buildCreateSuggestionTool2(connection, getRequestingUserId),
8171
+ buildVoteSuggestionTool2(connection, getRequestingUserId),
8172
+ buildAddDependencyTool2(connection, getRequestingUserId),
8173
+ buildRemoveDependencyTool2(connection, getRequestingUserId)
7807
8174
  ];
7808
8175
  }
7809
- function buildMutationTools2(connection) {
8176
+
8177
+ // src/tools/project-tools.ts
8178
+ function errorMessage(error) {
8179
+ return error instanceof Error ? error.message : "Unknown error";
8180
+ }
8181
+ function buildListTasksTool(connection) {
7810
8182
  const projectId = connection.projectId;
7811
- return [
7812
- defineTool(
7813
- "create_task",
7814
- "Create a new task in the project.",
7815
- {
7816
- title: z13.string().describe("Task title"),
7817
- description: z13.string().optional().describe("Task description"),
7818
- plan: z13.string().optional().describe("Implementation plan in markdown"),
7819
- status: z13.string().optional().describe("Initial status (default: Planning)")
7820
- },
7821
- async (params) => {
7822
- try {
7823
- const result = await connection.call("createProjectTask", { projectId, ...params });
7824
- return textResult(`Task created: ${result.slug} (ID: ${result.id})`);
7825
- } catch (error) {
7826
- return textResult(
7827
- `Failed to create task: ${error instanceof Error ? error.message : "Unknown error"}`
7828
- );
7829
- }
8183
+ return defineTool(
8184
+ "list_tasks",
8185
+ "List tasks in the project. Optionally filter by status or assignee.",
8186
+ {
8187
+ status: z14.string().optional().describe("Filter by task status"),
8188
+ assigneeId: z14.string().optional().describe("Filter by assigned user ID"),
8189
+ limit: z14.number().optional().describe("Max number of tasks to return (default 50)")
8190
+ },
8191
+ async (params) => {
8192
+ try {
8193
+ const tasks = await connection.call("listProjectTasks", { projectId, ...params });
8194
+ return textResult(JSON.stringify(tasks, null, 2));
8195
+ } catch (error) {
8196
+ return textResult(`Failed to list tasks: ${errorMessage(error)}`);
7830
8197
  }
7831
- ),
7832
- defineTool(
7833
- "update_project_task",
7834
- "Update an existing task's title, description, plan, status, or assignee. Project-runner scope.",
7835
- {
7836
- task_id: z13.string().describe("The task ID to update"),
7837
- title: z13.string().optional().describe("New title"),
7838
- description: z13.string().optional().describe("New description"),
7839
- plan: z13.string().optional().describe("New plan in markdown"),
7840
- status: z13.string().optional().describe("New status"),
7841
- assignedUserId: z13.string().nullable().optional().describe("Assign to user ID, or null to unassign")
7842
- },
7843
- async ({ task_id, ...fields }) => {
7844
- try {
7845
- await connection.call("updateProjectTask", { projectId, taskId: task_id, ...fields });
7846
- return textResult("Task updated successfully.");
7847
- } catch (error) {
7848
- return textResult(
7849
- `Failed to update task: ${error instanceof Error ? error.message : "Unknown error"}`
7850
- );
8198
+ },
8199
+ { annotations: { readOnlyHint: true } }
8200
+ );
8201
+ }
8202
+ function buildGetProjectTaskTool(connection) {
8203
+ const projectId = connection.projectId;
8204
+ return defineTool(
8205
+ "get_project_task",
8206
+ "Get detailed information about a task in this project (chat messages, child tasks, session). Project-runner scope.",
8207
+ { task_id: z14.string().describe("The task ID to look up") },
8208
+ async ({ task_id }) => {
8209
+ try {
8210
+ const task = await connection.call("getProjectTask", { projectId, taskId: task_id });
8211
+ return textResult(JSON.stringify(task, null, 2));
8212
+ } catch (error) {
8213
+ return textResult(`Failed to get task: ${errorMessage(error)}`);
8214
+ }
8215
+ },
8216
+ { annotations: { readOnlyHint: true } }
8217
+ );
8218
+ }
8219
+ function buildSearchTasksTool(connection) {
8220
+ const projectId = connection.projectId;
8221
+ return defineTool(
8222
+ "search_tasks",
8223
+ "Search tasks by tags, text query, or status filters.",
8224
+ {
8225
+ tagNames: z14.array(z14.string()).optional().describe("Filter by tag names"),
8226
+ searchQuery: z14.string().optional().describe("Text search in title/description"),
8227
+ statusFilters: z14.array(z14.string()).optional().describe("Filter by statuses"),
8228
+ limit: z14.number().optional().describe("Max results (default 20)")
8229
+ },
8230
+ async (params) => {
8231
+ try {
8232
+ const tasks = await connection.call("searchProjectTasks", { projectId, ...params });
8233
+ return textResult(JSON.stringify(tasks, null, 2));
8234
+ } catch (error) {
8235
+ return textResult(`Failed to search tasks: ${errorMessage(error)}`);
8236
+ }
8237
+ },
8238
+ { annotations: { readOnlyHint: true } }
8239
+ );
8240
+ }
8241
+ function buildListSubtasksTool2(connection) {
8242
+ const projectId = connection.projectId;
8243
+ return defineTool(
8244
+ "list_subtasks",
8245
+ "List the immediate child tasks under a parent task. Use to coordinate subtask work \u2014 see status, ordering, and PR state.",
8246
+ { task_id: z14.string().describe("Parent task ID") },
8247
+ async ({ task_id }) => {
8248
+ try {
8249
+ const subtasks = await connection.call("listProjectSubtasks", {
8250
+ projectId,
8251
+ taskId: task_id
8252
+ });
8253
+ return textResult(JSON.stringify(subtasks, null, 2));
8254
+ } catch (error) {
8255
+ return textResult(`Failed to list subtasks: ${errorMessage(error)}`);
8256
+ }
8257
+ },
8258
+ { annotations: { readOnlyHint: true } }
8259
+ );
8260
+ }
8261
+ function buildListTagsTool(connection) {
8262
+ const projectId = connection.projectId;
8263
+ return defineTool(
8264
+ "list_tags",
8265
+ "List all tags available in the project.",
8266
+ {},
8267
+ async () => {
8268
+ try {
8269
+ const tags = await connection.call("listProjectTags", { projectId });
8270
+ return textResult(JSON.stringify(tags, null, 2));
8271
+ } catch (error) {
8272
+ return textResult(`Failed to list tags: ${errorMessage(error)}`);
8273
+ }
8274
+ },
8275
+ { annotations: { readOnlyHint: true } }
8276
+ );
8277
+ }
8278
+ function buildGetProjectSummaryTool(connection) {
8279
+ const projectId = connection.projectId;
8280
+ return defineTool(
8281
+ "get_project_summary",
8282
+ "Get a summary of the project including task counts by status and active builds.",
8283
+ {},
8284
+ async () => {
8285
+ try {
8286
+ const summary = await connection.call("getProjectSummary", { projectId });
8287
+ return textResult(JSON.stringify(summary, null, 2));
8288
+ } catch (error) {
8289
+ return textResult(`Failed to get project summary: ${errorMessage(error)}`);
8290
+ }
8291
+ },
8292
+ { annotations: { readOnlyHint: true } }
8293
+ );
8294
+ }
8295
+ function buildPostToChatTool2(connection, getRequestingUserId) {
8296
+ const projectId = connection.projectId;
8297
+ return defineTool(
8298
+ "post_to_chat",
8299
+ "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.",
8300
+ {
8301
+ message: z14.string().describe("The message content to post"),
8302
+ task_id: z14.string().optional().describe("Task ID to post into a specific task's chat. Omit for the project chat.")
8303
+ },
8304
+ async ({ message, task_id }) => {
8305
+ try {
8306
+ const requestingUserId = getRequestingUserId();
8307
+ if (task_id) {
8308
+ await connection.call("postToProjectTaskChat", {
8309
+ projectId,
8310
+ taskId: task_id,
8311
+ content: message,
8312
+ ...requestingUserId ? { requestingUserId } : {}
8313
+ });
8314
+ return textResult(JSON.stringify({ posted: true, target: `task:${task_id}` }));
7851
8315
  }
8316
+ await connection.call("postProjectAgentMessage", { projectId, content: message });
8317
+ return textResult(JSON.stringify({ posted: true, target: "project" }));
8318
+ } catch (error) {
8319
+ return textResult(`Failed to post message: ${errorMessage(error)}`);
7852
8320
  }
7853
- )
7854
- ];
8321
+ }
8322
+ );
8323
+ }
8324
+ function buildReadChatHistoryTool(connection) {
8325
+ const projectId = connection.projectId;
8326
+ return defineTool(
8327
+ "read_chat_history",
8328
+ "Read recent messages from a chat. Omit chat_id to read the project chat; pass a chat_id to read a specific chat.",
8329
+ {
8330
+ chat_id: z14.string().optional().describe("Chat ID to read. Omit for the project chat."),
8331
+ limit: z14.number().optional().describe("Number of recent messages to fetch (default 50)")
8332
+ },
8333
+ async ({ chat_id, limit }) => {
8334
+ try {
8335
+ const messages = await connection.call("getProjectChatHistory", {
8336
+ projectId,
8337
+ ...chat_id !== void 0 && { chatId: chat_id },
8338
+ ...limit !== void 0 && { limit }
8339
+ });
8340
+ return textResult(JSON.stringify(messages, null, 2));
8341
+ } catch (error) {
8342
+ return textResult(`Failed to read chat history: ${errorMessage(error)}`);
8343
+ }
8344
+ },
8345
+ { annotations: { readOnlyHint: true } }
8346
+ );
8347
+ }
8348
+ function buildReadTaskChatTool2(connection) {
8349
+ const projectId = connection.projectId;
8350
+ return defineTool(
8351
+ "read_task_chat",
8352
+ "Read recent human/user chat messages for a specific task in this project. For agent execution logs use get_task_logs.",
8353
+ {
8354
+ task_id: z14.string().describe("The task ID whose chat to read"),
8355
+ limit: z14.number().optional().describe("Number of recent messages to fetch (default 20)")
8356
+ },
8357
+ async ({ task_id, limit }) => {
8358
+ try {
8359
+ const messages = await connection.call("getProjectTaskChat", {
8360
+ projectId,
8361
+ taskId: task_id,
8362
+ ...limit !== void 0 && { limit }
8363
+ });
8364
+ return textResult(JSON.stringify(messages, null, 2));
8365
+ } catch (error) {
8366
+ return textResult(`Failed to read task chat: ${errorMessage(error)}`);
8367
+ }
8368
+ },
8369
+ { annotations: { readOnlyHint: true } }
8370
+ );
7855
8371
  }
7856
- function buildProjectTools(connection) {
8372
+ function buildGetTaskLogsTool(connection) {
8373
+ const projectId = connection.projectId;
8374
+ return defineTool(
8375
+ "get_task_logs",
8376
+ "Read CLI execution logs for a task \u2014 agent reasoning, tool calls, and setup/dev-server output. For human chat use read_task_chat.",
8377
+ {
8378
+ task_id: z14.string().describe("Task ID to read logs from"),
8379
+ source: z14.enum(["agent", "application"]).optional().describe("Filter by log source. Omit for all logs."),
8380
+ limit: z14.number().optional().describe("Max number of log entries to return (default 50, max 500).")
8381
+ },
8382
+ async ({ task_id, source, limit }) => {
8383
+ try {
8384
+ const logs = await connection.call("getProjectTaskCli", {
8385
+ projectId,
8386
+ taskId: task_id,
8387
+ ...source !== void 0 && { source },
8388
+ ...limit !== void 0 && { limit }
8389
+ });
8390
+ return textResult(JSON.stringify(logs, null, 2));
8391
+ } catch (error) {
8392
+ return textResult(`Failed to fetch task logs: ${errorMessage(error)}`);
8393
+ }
8394
+ },
8395
+ { annotations: { readOnlyHint: true } }
8396
+ );
8397
+ }
8398
+ function buildProjectTools(connection, getRequestingUserId = () => void 0) {
7857
8399
  return [
7858
- ...buildTaskListTools(connection),
7859
- ...buildProjectInfoTools(connection),
7860
- ...buildMutationTools2(connection)
8400
+ buildListTasksTool(connection),
8401
+ buildGetProjectTaskTool(connection),
8402
+ buildSearchTasksTool(connection),
8403
+ buildListSubtasksTool2(connection),
8404
+ buildListTagsTool(connection),
8405
+ buildGetProjectSummaryTool(connection),
8406
+ buildPostToChatTool2(connection, getRequestingUserId),
8407
+ buildReadChatHistoryTool(connection),
8408
+ buildReadTaskChatTool2(connection),
8409
+ buildGetTaskLogsTool(connection),
8410
+ ...buildProjectActionTools(connection, getRequestingUserId)
7861
8411
  ];
7862
8412
  }
7863
8413
 
@@ -7923,13 +8473,13 @@ async function fetchContext(connection, chatId) {
7923
8473
  }
7924
8474
  return { agentCtx, chatHistory };
7925
8475
  }
7926
- function buildChatQueryOptions(agentCtx, projectDir, connection) {
8476
+ function buildChatQueryOptions(agentCtx, projectDir, connection, getRequestingUserId) {
7927
8477
  const model = agentCtx?.model || FALLBACK_MODEL;
7928
8478
  const settings = agentCtx?.agentSettings ?? {};
7929
8479
  const harness = createHarness();
7930
8480
  const mcpServer = harness.createMcpServer({
7931
8481
  name: "conveyor",
7932
- tools: buildProjectTools(connection)
8482
+ tools: buildProjectTools(connection, getRequestingUserId)
7933
8483
  });
7934
8484
  return {
7935
8485
  options: {
@@ -7998,7 +8548,13 @@ function emitResultCost(event, connection) {
7998
8548
  }
7999
8549
  async function runChatQuery(message, connection, projectDir, sessionId) {
8000
8550
  const { agentCtx, chatHistory } = await fetchContext(connection, message.chatId);
8001
- const { options, harness } = buildChatQueryOptions(agentCtx, projectDir, connection);
8551
+ const requestingUserId = message.userId;
8552
+ const { options, harness } = buildChatQueryOptions(
8553
+ agentCtx,
8554
+ projectDir,
8555
+ connection,
8556
+ () => requestingUserId
8557
+ );
8002
8558
  const prompt = buildPrompt(message, chatHistory);
8003
8559
  connection.emitStatus("busy");
8004
8560
  const events = harness.executeQuery({
@@ -8514,6 +9070,10 @@ var ProjectRunner = class {
8514
9070
  stopping = false;
8515
9071
  resolveLifecycle = null;
8516
9072
  chatSessionIds = /* @__PURE__ */ new Map();
9073
+ /** ProjectCodespaceSession id assigned at register time. Used to filter
9074
+ * targeted incoming chat messages so we only react to ones addressed
9075
+ * to this specific runner. */
9076
+ projectSessionId = null;
8517
9077
  startCmd = { child: null, running: false };
8518
9078
  setupComplete = false;
8519
9079
  branchSwitchCommand;
@@ -8553,6 +9113,7 @@ var ProjectRunner = class {
8553
9113
  projectId: this.connection.projectId,
8554
9114
  capabilities: ["task", "pm", "code-review", "audit"]
8555
9115
  });
9116
+ this.projectSessionId = registration.sessionId;
8556
9117
  this.branchSwitchCommand = registration.branchSwitchCommand ?? process.env.CONVEYOR_BRANCH_SWITCH_COMMAND;
8557
9118
  logger10.info("Registered as project agent", { agentName: registration.agentName });
8558
9119
  try {
@@ -8659,6 +9220,9 @@ var ProjectRunner = class {
8659
9220
  );
8660
9221
  this.connection.onShutdown(() => void this.stop());
8661
9222
  this.connection.onChatMessage((msg) => {
9223
+ if (msg.targetSessionId && this.projectSessionId && msg.targetSessionId !== this.projectSessionId) {
9224
+ return;
9225
+ }
8662
9226
  const chatId = msg.chatId ?? "default";
8663
9227
  const existingSessionId = this.chatSessionIds.get(chatId);
8664
9228
  void handleProjectChatMessage(msg, this.connection, this.projectDir, existingSessionId).then(
@@ -8697,10 +9261,11 @@ var ProjectRunner = class {
8697
9261
  }
8698
9262
  async handleReconnect() {
8699
9263
  try {
8700
- await this.connection.call("registerProjectAgent", {
9264
+ const registration = await this.connection.call("registerProjectAgent", {
8701
9265
  projectId: this.connection.projectId,
8702
9266
  capabilities: ["task", "pm", "code-review", "audit"]
8703
9267
  });
9268
+ this.projectSessionId = registration.sessionId;
8704
9269
  this.connection.emitStatus(this.activeAgents.size > 0 ? "busy" : "idle");
8705
9270
  logger10.info("Re-registered after reconnect");
8706
9271
  } catch (error) {
@@ -8860,4 +9425,4 @@ export {
8860
9425
  buildSessionPreviewPorts,
8861
9426
  loadConveyorConfig
8862
9427
  };
8863
- //# sourceMappingURL=chunk-RX7NQCWK.js.map
9428
+ //# sourceMappingURL=chunk-24NCVM4T.js.map