@rallycry/conveyor-agent 6.4.0 → 6.4.2

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.
@@ -176,6 +176,15 @@ function createFollowUpTask(socket, data) {
176
176
  function queryGcpLogs(socket, params) {
177
177
  return emitRpc(requireSocket(socket), "agentRunner:queryGcpLogs", params);
178
178
  }
179
+ function createSuggestion(socket, data) {
180
+ return emitRpc(requireSocket(socket), "agentRunner:createSuggestion", data);
181
+ }
182
+ function voteSuggestion(socket, suggestionId, value) {
183
+ return emitRpc(requireSocket(socket), "agentRunner:voteSuggestion", { suggestionId, value });
184
+ }
185
+ function getSuggestions(socket, status, limit) {
186
+ return emitRpc(requireSocket(socket), "agentRunner:getSuggestions", { status, limit });
187
+ }
179
188
 
180
189
  // src/connection/task-connection.ts
181
190
  var ConveyorConnection = class _ConveyorConnection {
@@ -245,9 +254,18 @@ var ConveyorConnection = class _ConveyorConnection {
245
254
  this.chatMessageCallback({ content: "", userId: "system" });
246
255
  }
247
256
  });
248
- this.socket.on("agentRunner:updateApiKey", (data) => {
249
- process.env.ANTHROPIC_API_KEY = data.apiKey;
250
- });
257
+ this.socket.on(
258
+ "agentRunner:updateApiKey",
259
+ (data) => {
260
+ if (data.isSubscription) {
261
+ process.env.CLAUDE_CODE_OAUTH_TOKEN = data.apiKey;
262
+ delete process.env.ANTHROPIC_API_KEY;
263
+ } else {
264
+ process.env.ANTHROPIC_API_KEY = data.apiKey;
265
+ delete process.env.CLAUDE_CODE_OAUTH_TOKEN;
266
+ }
267
+ }
268
+ );
251
269
  this.socket.on(
252
270
  "agentRunner:runAuthTokenCommand",
253
271
  (data, cb) => this.handleRunAuthTokenCommand(data.userEmail, cb)
@@ -539,6 +557,15 @@ var ConveyorConnection = class _ConveyorConnection {
539
557
  queryGcpLogs(params) {
540
558
  return queryGcpLogs(this.socket, params);
541
559
  }
560
+ createSuggestion(title, description, tagNames) {
561
+ return createSuggestion(this.socket, { title, description, tagNames });
562
+ }
563
+ voteSuggestion(suggestionId, value) {
564
+ return voteSuggestion(this.socket, suggestionId, value);
565
+ }
566
+ getSuggestions(status, limit) {
567
+ return getSuggestions(this.socket, status, limit);
568
+ }
542
569
  disconnect() {
543
570
  this.flushEvents();
544
571
  if (this.socket) {
@@ -956,14 +983,14 @@ function pushToOrigin(cwd) {
956
983
  const currentBranch = getCurrentBranch(cwd);
957
984
  if (!currentBranch) return false;
958
985
  try {
959
- execSync2(`git push origin ${currentBranch}`, {
986
+ execSync2(`git push --no-verify origin ${currentBranch}`, {
960
987
  cwd,
961
988
  stdio: ["ignore", "pipe", "ignore"]
962
989
  });
963
990
  return true;
964
991
  } catch {
965
992
  try {
966
- execSync2(`git push --force-with-lease origin ${currentBranch}`, {
993
+ execSync2(`git push --no-verify --force-with-lease origin ${currentBranch}`, {
967
994
  cwd,
968
995
  stdio: ["ignore", "pipe", "ignore"]
969
996
  });
@@ -2485,26 +2512,49 @@ function detectRelaunchScenario(context, trustChatHistory = false) {
2485
2512
  const hasNewUserMessages = messagesAfterAgent.some((m) => m.role === "user");
2486
2513
  return hasNewUserMessages ? "feedback_relaunch" : "idle_relaunch";
2487
2514
  }
2488
- function buildRelaunchWithSession(mode, context, agentMode, isAuto) {
2489
- const scenario = detectRelaunchScenario(context);
2490
- if (!context.claudeSessionId || scenario === "fresh") return null;
2515
+ function buildPmRelaunchParts(context, lastAgentIdx, isAuto) {
2491
2516
  const parts = [];
2492
- const lastAgentIdx = findLastAgentMessageIndex2(context.chatHistory);
2493
- if (mode === "pm") {
2494
- const newMessages = context.chatHistory.slice(lastAgentIdx + 1).filter((m) => m.role === "user");
2495
- if (newMessages.length > 0) {
2517
+ const newMessages = context.chatHistory.slice(lastAgentIdx + 1).filter((m) => m.role === "user");
2518
+ if (newMessages.length > 0) {
2519
+ parts.push(
2520
+ `You have been relaunched. Here are new messages since your last session:`,
2521
+ ...newMessages.map((m) => `[${m.userName ?? "user"}]: ${m.content}`)
2522
+ );
2523
+ } else {
2524
+ parts.push(`You have been relaunched. No new messages since your last session.`);
2525
+ }
2526
+ if (isAuto) {
2527
+ if (context.plan?.trim()) {
2496
2528
  parts.push(
2497
- `You have been relaunched. Here are new messages since your last session:`,
2498
- ...newMessages.map((m) => `[${m.userName ?? "user"}]: ${m.content}`)
2529
+ `
2530
+ You are in auto mode. A plan already exists for this task.`,
2531
+ `Verify that story points, title, and tags are set via get_task_plan, then call ExitPlanMode immediately to transition to building.`,
2532
+ `Do NOT wait for team input \u2014 proceed autonomously.`
2499
2533
  );
2500
2534
  } else {
2501
- parts.push(`You have been relaunched. No new messages since your last session.`);
2535
+ parts.push(
2536
+ `
2537
+ You are in auto mode. Continue planning autonomously.`,
2538
+ `When the plan and all required properties are set, call ExitPlanMode to transition to building.`,
2539
+ `Do NOT wait for team input \u2014 proceed autonomously.`
2540
+ );
2502
2541
  }
2542
+ } else {
2503
2543
  parts.push(
2504
2544
  `
2505
2545
  You are the project manager for this task.`,
2506
2546
  `Review the context above and wait for the team to provide instructions before taking action.`
2507
2547
  );
2548
+ }
2549
+ return parts;
2550
+ }
2551
+ function buildRelaunchWithSession(mode, context, agentMode, isAuto) {
2552
+ const scenario = detectRelaunchScenario(context);
2553
+ if (!context.claudeSessionId || scenario === "fresh") return null;
2554
+ const parts = [];
2555
+ const lastAgentIdx = findLastAgentMessageIndex2(context.chatHistory);
2556
+ if (mode === "pm") {
2557
+ parts.push(...buildPmRelaunchParts(context, lastAgentIdx, isAuto));
2508
2558
  } else if (scenario === "feedback_relaunch") {
2509
2559
  const newMessages = context.chatHistory.slice(lastAgentIdx + 1).filter((m) => m.role === "user");
2510
2560
  parts.push(
@@ -2673,6 +2723,13 @@ CRITICAL: You are in Auto mode. Do NOT report status, ask for confirmation, or g
2673
2723
  ];
2674
2724
  }
2675
2725
  if (isAutoMode && isPm) {
2726
+ if (context.plan?.trim()) {
2727
+ return [
2728
+ `You are operating autonomously. A plan already exists for this task.`,
2729
+ `Verify that story points, title, and tags are set, then call ExitPlanMode immediately to transition to building.`,
2730
+ `Do NOT re-plan or wait for team input \u2014 proceed autonomously.`
2731
+ ];
2732
+ }
2676
2733
  return [
2677
2734
  `You are operating autonomously. Begin planning immediately.`,
2678
2735
  `1. Explore the codebase to understand the architecture and relevant files`,
@@ -2751,6 +2808,50 @@ Address the requested changes directly. Do NOT re-investigate the codebase from
2751
2808
  }
2752
2809
  return parts;
2753
2810
  }
2811
+ function buildIdleRelaunchInstructions(context, isPm, agentMode, isAuto) {
2812
+ if (isPm && agentMode === "auto" && context.status === "Planning") {
2813
+ if (context.plan?.trim()) {
2814
+ return [
2815
+ `You were relaunched in auto mode. A plan already exists for this task.`,
2816
+ `Verify that story points, title, and tags are set, then call ExitPlanMode immediately to transition to building.`,
2817
+ `Do NOT wait for instructions or go idle \u2014 you are in auto mode.`
2818
+ ];
2819
+ }
2820
+ return [
2821
+ `You were relaunched in auto mode. Continue planning autonomously.`,
2822
+ `When the plan and all required properties are set, call ExitPlanMode to transition to building.`,
2823
+ `Do NOT wait for instructions or go idle \u2014 you are in auto mode.`
2824
+ ];
2825
+ }
2826
+ if (isPm && !(agentMode === "building" || agentMode === "review" || agentMode === "auto")) {
2827
+ return [
2828
+ `You were relaunched but no new instructions have been given since your last run.`,
2829
+ `You are the project manager for this task.`,
2830
+ `Wait for the team to provide instructions before taking action.`
2831
+ ];
2832
+ }
2833
+ const isAutoMode = agentMode === "auto" || agentMode === "building" && isAuto;
2834
+ const parts = [
2835
+ `You were relaunched but no new instructions have been given since your last run.`,
2836
+ `Work on the git branch "${context.githubBranch}". Stay on this branch \u2014 do not checkout or create other branches.`,
2837
+ `Run \`git log --oneline -10\` to review what you already committed, then verify the current state is correct.`
2838
+ ];
2839
+ if (isAutoMode) {
2840
+ parts.push(
2841
+ `If work is incomplete, continue implementing the plan. When finished, use mcp__conveyor__create_pull_request to open a PR.`,
2842
+ `Do NOT go idle or wait for instructions \u2014 you are in auto mode.`
2843
+ );
2844
+ } else {
2845
+ parts.push(
2846
+ `Reply with a brief status update summarizing where things stand (visible in chat).`,
2847
+ `Then wait for further instructions \u2014 do NOT redo work that was already completed.`
2848
+ );
2849
+ }
2850
+ if (context.githubPRUrl) {
2851
+ parts.push(`An existing PR is open at ${context.githubPRUrl}. Do not create a new PR.`);
2852
+ }
2853
+ return parts;
2854
+ }
2754
2855
  function buildInstructions(mode, context, scenario, agentMode, isAuto) {
2755
2856
  const parts = [`
2756
2857
  ## Instructions`];
@@ -2764,44 +2865,7 @@ function buildInstructions(mode, context, scenario, agentMode, isAuto) {
2764
2865
  return parts;
2765
2866
  }
2766
2867
  if (scenario === "idle_relaunch") {
2767
- if (isPm && (agentMode === "building" || agentMode === "review" || agentMode === "auto")) {
2768
- parts.push(
2769
- `You were relaunched but no new instructions have been given since your last run.`,
2770
- `Work on the git branch "${context.githubBranch}". Stay on this branch \u2014 do not checkout or create other branches.`,
2771
- `Run \`git log --oneline -10\` to review what you already committed, then verify the current state is correct.`,
2772
- `Reply with a brief status update summarizing where things stand (visible in chat).`,
2773
- `Then wait for further instructions \u2014 do NOT redo work that was already completed.`
2774
- );
2775
- if (context.githubPRUrl) {
2776
- parts.push(`An existing PR is open at ${context.githubPRUrl}. Do not create a new PR.`);
2777
- }
2778
- } else if (isPm) {
2779
- parts.push(
2780
- `You were relaunched but no new instructions have been given since your last run.`,
2781
- `You are the project manager for this task.`,
2782
- `Wait for the team to provide instructions before taking action.`
2783
- );
2784
- } else {
2785
- parts.push(
2786
- `You were relaunched but no new instructions have been given since your last run.`,
2787
- `Work on the git branch "${context.githubBranch}". Stay on this branch \u2014 do not checkout or create other branches.`,
2788
- `Run \`git log --oneline -10\` to review what you already committed, then verify the current state is correct.`
2789
- );
2790
- if (agentMode === "auto" || agentMode === "building" && isAuto) {
2791
- parts.push(
2792
- `If work is incomplete, continue implementing the plan. When finished, use mcp__conveyor__create_pull_request to open a PR.`,
2793
- `Do NOT go idle or wait for instructions \u2014 you are in auto mode.`
2794
- );
2795
- } else {
2796
- parts.push(
2797
- `Reply with a brief status update summarizing where things stand (visible in chat).`,
2798
- `Then wait for further instructions \u2014 do NOT redo work that was already completed.`
2799
- );
2800
- }
2801
- if (context.githubPRUrl) {
2802
- parts.push(`An existing PR is open at ${context.githubPRUrl}. Do not create a new PR.`);
2803
- }
2804
- }
2868
+ parts.push(...buildIdleRelaunchInstructions(context, isPm, agentMode, isAuto));
2805
2869
  return parts;
2806
2870
  }
2807
2871
  parts.push(...buildFeedbackInstructions(context, isPm));
@@ -3268,6 +3332,76 @@ function buildGetDependenciesTool(connection) {
3268
3332
  { annotations: { readOnlyHint: true } }
3269
3333
  );
3270
3334
  }
3335
+ function buildCreateSuggestionTool(connection) {
3336
+ return defineTool(
3337
+ "create_suggestion",
3338
+ "Suggest a feature, improvement, or idea for the project. If you want to recommend something \u2014 a document, a rule, a feature, a task, an optimization \u2014 use this tool. If a similar suggestion already exists, your vote will be added to it instead of creating a duplicate.",
3339
+ {
3340
+ title: z.string().describe("Short title for the suggestion"),
3341
+ description: z.string().optional().describe("Details about the suggestion"),
3342
+ tag_names: z.array(z.string()).optional().describe("Tag names to categorize the suggestion")
3343
+ },
3344
+ async ({ title, description, tag_names }) => {
3345
+ try {
3346
+ const result = await connection.createSuggestion(title, description, tag_names);
3347
+ if (result.merged) {
3348
+ return textResult(
3349
+ `Your suggestion was merged into an existing one (ID: ${result.mergedIntoId ?? result.id}). Your upvote has been recorded.`
3350
+ );
3351
+ }
3352
+ return textResult(`Suggestion created (ID: ${result.id}).`);
3353
+ } catch (error) {
3354
+ return textResult(
3355
+ `Failed to create suggestion: ${error instanceof Error ? error.message : "Unknown error"}`
3356
+ );
3357
+ }
3358
+ }
3359
+ );
3360
+ }
3361
+ function buildVoteSuggestionTool(connection) {
3362
+ return defineTool(
3363
+ "vote_suggestion",
3364
+ "Vote on a project suggestion. Use +1 to upvote or -1 to downvote.",
3365
+ {
3366
+ suggestion_id: z.string().describe("The suggestion ID to vote on"),
3367
+ value: z.number().refine((v) => v === 1 || v === -1, { message: "Value must be 1 or -1" }).describe("+1 to upvote, -1 to downvote")
3368
+ },
3369
+ async ({ suggestion_id, value }) => {
3370
+ try {
3371
+ const result = await connection.voteSuggestion(suggestion_id, value);
3372
+ return textResult(`Vote recorded. Current score: ${result.score}`);
3373
+ } catch (error) {
3374
+ return textResult(
3375
+ `Failed to vote: ${error instanceof Error ? error.message : "Unknown error"}`
3376
+ );
3377
+ }
3378
+ }
3379
+ );
3380
+ }
3381
+ function buildGetSuggestionsTool(connection) {
3382
+ return defineTool(
3383
+ "get_suggestions",
3384
+ "List project suggestions sorted by vote score. Use this to see what the team thinks is important.",
3385
+ {
3386
+ status: z.string().optional().describe("Filter by status: Open, Accepted, Rejected, Implemented"),
3387
+ limit: z.number().optional().describe("Max results (default 20)")
3388
+ },
3389
+ async ({ status, limit }) => {
3390
+ try {
3391
+ const suggestions = await connection.getSuggestions(status, limit);
3392
+ if (suggestions.length === 0) {
3393
+ return textResult("No suggestions found.");
3394
+ }
3395
+ return textResult(JSON.stringify(suggestions, null, 2));
3396
+ } catch (error) {
3397
+ return textResult(
3398
+ `Failed to get suggestions: ${error instanceof Error ? error.message : "Unknown error"}`
3399
+ );
3400
+ }
3401
+ },
3402
+ { annotations: { readOnlyHint: true } }
3403
+ );
3404
+ }
3271
3405
  function buildCreateFollowUpTaskTool(connection) {
3272
3406
  return defineTool(
3273
3407
  "create_follow_up_task",
@@ -3313,7 +3447,10 @@ function buildCommonTools(connection, config) {
3313
3447
  buildAddDependencyTool(connection),
3314
3448
  buildRemoveDependencyTool(connection),
3315
3449
  buildGetDependenciesTool(connection),
3316
- buildCreateFollowUpTaskTool(connection)
3450
+ buildCreateFollowUpTaskTool(connection),
3451
+ buildCreateSuggestionTool(connection),
3452
+ buildVoteSuggestionTool(connection),
3453
+ buildGetSuggestionsTool(connection)
3317
3454
  ];
3318
3455
  if (process.env.CLAUDESPACE_NAME) {
3319
3456
  tools.push(buildScaleUpResourcesTool(connection));
@@ -6052,6 +6189,19 @@ var AgentRunner = class {
6052
6189
  return;
6053
6190
  }
6054
6191
  this.conveyorConfig = result.conveyorConfig;
6192
+ } else if (process.env.CLAUDESPACE_NAME) {
6193
+ void runSetupSafe(
6194
+ this.config,
6195
+ this.connection,
6196
+ this.callbacks,
6197
+ this.setupLog,
6198
+ this.effectiveAgentMode,
6199
+ // no-op setState — agent state is managed by the main flow
6200
+ () => Promise.resolve()
6201
+ ).then((result) => {
6202
+ if (result.ok) this.conveyorConfig = result.conveyorConfig;
6203
+ }).catch(() => {
6204
+ });
6055
6205
  }
6056
6206
  this.tryInitWorktree();
6057
6207
  if (!await this.fetchAndInitContext()) return;
@@ -6085,17 +6235,45 @@ var AgentRunner = class {
6085
6235
  }
6086
6236
  this.taskContext._runnerSessionId = randomUUID2();
6087
6237
  if (this.taskContext.agentMode) this.agentMode = this.taskContext.agentMode;
6088
- const pastPlanning = this.taskContext.status !== "Planning" && this.taskContext.status !== "Unidentified";
6089
- if (this.agentMode === "auto" && pastPlanning) {
6090
- this.hasExitedPlanMode = true;
6091
- this.agentMode = "building";
6092
- const builderModel = this.taskContext.builderModel ?? this.taskContext.model;
6093
- if (builderModel) this.taskContext.model = builderModel;
6094
- }
6238
+ await this.tryAutoModeBypass();
6095
6239
  this.logEffectiveSettings();
6096
- if (process.env.CODESPACES === "true") unshallowRepo(this.config.workspaceDir);
6240
+ if (process.env.CODESPACES === "true" || process.env.CLAUDESPACE_NAME) {
6241
+ unshallowRepo(this.config.workspaceDir);
6242
+ }
6097
6243
  return true;
6098
6244
  }
6245
+ /**
6246
+ * For auto-mode tasks, bypass planning if the task is already past Planning
6247
+ * or if it's still in Planning but has all required properties set (e.g. from
6248
+ * a prior PM discovery session).
6249
+ */
6250
+ async tryAutoModeBypass() {
6251
+ if (this.agentMode !== "auto" || !this.taskContext) return;
6252
+ const pastPlanning = this.taskContext.status !== "Planning" && this.taskContext.status !== "Unidentified";
6253
+ if (pastPlanning) {
6254
+ this.transitionToBuilding();
6255
+ return;
6256
+ }
6257
+ if (this.taskContext.status !== "Planning") return;
6258
+ try {
6259
+ const props = await this.connection.getTaskProperties();
6260
+ const hasRequiredProps = !!props.plan?.trim() && !!props.storyPointId && !!props.title && props.title !== "Untitled";
6261
+ if (hasRequiredProps) {
6262
+ await this.connection.triggerIdentification();
6263
+ this.transitionToBuilding();
6264
+ this.connection.emitModeChanged(this.agentMode);
6265
+ this.connection.emitModeTransition({ fromMode: "auto", toMode: this.agentMode });
6266
+ }
6267
+ } catch {
6268
+ }
6269
+ }
6270
+ transitionToBuilding() {
6271
+ if (!this.taskContext) return;
6272
+ this.hasExitedPlanMode = true;
6273
+ this.agentMode = this.taskContext.isParentTask ? "review" : "building";
6274
+ const builderModel = this.taskContext.builderModel ?? this.taskContext.model;
6275
+ if (builderModel) this.taskContext.model = builderModel;
6276
+ }
6099
6277
  tryPostContextWorktree() {
6100
6278
  if (process.env.CODESPACES !== "true" && process.env.CONVEYOR_USE_WORKTREE !== "false" && !this.worktreeActive && this.taskContext?.useWorktree) {
6101
6279
  this.activateWorktree("[conveyor] Using worktree (from task config):");
@@ -6425,7 +6603,8 @@ var AgentRunner = class {
6425
6603
  return;
6426
6604
  }
6427
6605
  if (newAgentMode !== "auto") return;
6428
- const pastPlanning = this.taskContext?.status !== "Planning" && this.taskContext?.status !== "Unidentified";
6606
+ if (!this.taskContext) return;
6607
+ const pastPlanning = this.taskContext.status !== "Planning" && this.taskContext.status !== "Unidentified";
6429
6608
  if (pastPlanning) {
6430
6609
  this.hasExitedPlanMode = true;
6431
6610
  }
@@ -8017,4 +8196,4 @@ export {
8017
8196
  ProjectRunner,
8018
8197
  FileCache
8019
8198
  };
8020
- //# sourceMappingURL=chunk-WBBX5AIX.js.map
8199
+ //# sourceMappingURL=chunk-XHJ2Z4AD.js.map