@rallycry/conveyor-agent 10.13.54 → 10.13.56

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.
@@ -3,11 +3,13 @@ import {
3
3
  DEFAULT_RETRY_DELAY_MS,
4
4
  FETCH_TIMEOUT_MS,
5
5
  GIT_PREP_MAX_RETRIES,
6
+ gitCredentialHelper,
6
7
  mapChatHistory,
7
8
  readAgentVersion,
8
9
  registerBootMilestoneSocketFallback,
9
- reportBootMilestone
10
- } from "./chunk-KCB7CSWJ.js";
10
+ reportBootMilestone,
11
+ writeGitCredential
12
+ } from "./chunk-7765OQU5.js";
11
13
  import {
12
14
  LoopLagMonitor,
13
15
  buildConveyorSocketOptions,
@@ -1454,22 +1456,24 @@ async function isAuthError(cwd) {
1454
1456
  return errLooksLikeAuth(err);
1455
1457
  }
1456
1458
  }
1457
- async function updateRemoteToken(cwd, token) {
1459
+ async function updateRemoteCredential(cwd, credential) {
1458
1460
  try {
1459
- const url = await git(cwd, ["remote", "get-url", "origin"]);
1460
- const match = url.match(/github\.com[/:]([^/]+\/[^/]+?)(?:\.git)?\/?$/);
1461
- if (match) {
1462
- const repo = match[1].replace(/\.git$/, "");
1463
- await git(cwd, [
1464
- "remote",
1465
- "set-url",
1466
- "origin",
1467
- `https://x-access-token:${token}@github.com/${repo}.git`
1468
- ]);
1461
+ const currentUrl = await git(cwd, ["remote", "get-url", "origin"]);
1462
+ const cloneUrl = credential.cloneUrl ?? currentUrl;
1463
+ writeGitCredential(cwd, cloneUrl, credential);
1464
+ if (currentUrl !== cloneUrl) {
1465
+ await git(cwd, ["remote", "set-url", "origin", cloneUrl]);
1469
1466
  }
1467
+ await git(cwd, ["config", "--local", "credential.helper", gitCredentialHelper(cwd)]);
1470
1468
  } catch {
1471
1469
  }
1472
1470
  }
1471
+ async function updateRemoteToken(cwd, token) {
1472
+ const username = process.env.CONVEYOR_GIT_USERNAME || "x-access-token";
1473
+ const cloneUrl = process.env.CONVEYOR_GIT_CLONE_URL || void 0;
1474
+ await updateRemoteCredential(cwd, { username, secret: token, cloneUrl });
1475
+ process.env.CONVEYOR_GIT_SECRET = token;
1476
+ }
1473
1477
  function wipRefForBranch(branch) {
1474
1478
  return `conveyor-wip/${branch}`;
1475
1479
  }
@@ -2680,15 +2684,18 @@ var UpdateProjectTaskRequestSchema = z5.object({
2680
2684
  // Canonical risk level, or null to clear. Resolved to the project's
2681
2685
  // configured Risk row (by rank) in the handler.
2682
2686
  risk: riskLevelSchema.nullable().optional(),
2687
+ // Story-point value, or null to clear. Resolved to the project's configured
2688
+ // StoryPoint row in the handler, which rejects an unconfigured value.
2689
+ storyPointValue: z5.number().int().positive().nullable().optional(),
2683
2690
  assignedUserId: z5.string().nullish(),
2684
2691
  // Move to a different sub-project board, or null to move to the parent board.
2685
2692
  // Validated to belong to `projectId` in the handler.
2686
2693
  subProjectId: z5.string().nullable().optional(),
2687
2694
  requestingUserId: z5.string().optional()
2688
2695
  }).strict().refine(
2689
- (v) => v.title !== void 0 || v.description !== void 0 || v.plan !== void 0 || v.status !== void 0 || v.risk !== void 0 || v.assignedUserId !== void 0 || v.subProjectId !== void 0,
2696
+ (v) => v.title !== void 0 || v.description !== void 0 || v.plan !== void 0 || v.status !== void 0 || v.risk !== void 0 || v.storyPointValue !== void 0 || v.assignedUserId !== void 0 || v.subProjectId !== void 0,
2690
2697
  {
2691
- message: "update_task requires at least one field to change (title, description, plan, status, risk, assignedUserId, or subProjectId)"
2698
+ message: "update_task requires at least one field to change (title, description, plan, status, risk, storyPointValue, assignedUserId, or subProjectId)"
2692
2699
  }
2693
2700
  );
2694
2701
  var TransitionProjectTaskStatusRequestSchema = z5.object({
@@ -3934,6 +3941,15 @@ function mapToolPart(part) {
3934
3941
  }
3935
3942
  };
3936
3943
  }
3944
+ function stringifyToolOutput(output) {
3945
+ if (output === void 0 || output === null) return "";
3946
+ if (typeof output === "string") return output;
3947
+ try {
3948
+ return JSON.stringify(output);
3949
+ } catch {
3950
+ return String(output);
3951
+ }
3952
+ }
3937
3953
  function errorMessageOf(event) {
3938
3954
  if (event.type !== "error") return null;
3939
3955
  const error = event.error;
@@ -3985,19 +4001,44 @@ ${stderrTail.trim()}`);
3985
4001
 
3986
4002
  // src/harness/opencode/event-source.ts
3987
4003
  var MAX_TRACKED_MESSAGES = 200;
4004
+ var CHAT_TEXT_MAX = 16e3;
4005
+ var CHAT_TOOL_INPUT_MAX = 1900;
4006
+ var CHAT_TOOL_OUTPUT_MAX = 1900;
4007
+ function truncate(text, max) {
4008
+ return text.length > max ? `${text.slice(0, max)}\u2026` : text;
4009
+ }
4010
+ function compactJson(value) {
4011
+ try {
4012
+ return JSON.stringify(value ?? {}).slice(0, CHAT_TOOL_INPUT_MAX);
4013
+ } catch {
4014
+ return String(value).slice(0, CHAT_TOOL_INPUT_MAX);
4015
+ }
4016
+ }
4017
+ function partString(part, key) {
4018
+ const value = part[key];
4019
+ return typeof value === "string" && value.length > 0 ? value : null;
4020
+ }
4021
+ function relayedToolStatus(status) {
4022
+ return status === "running" || status === "completed" || status === "error" ? status : null;
4023
+ }
3988
4024
  var OpenCodeEventSource = class {
3989
- constructor(emit, onSessionId) {
4025
+ constructor(emit, onSessionId, emitChat) {
3990
4026
  this.emit = emit;
3991
4027
  this.onSessionId = onSessionId;
4028
+ this.emitChat = emitChat;
3992
4029
  }
3993
4030
  emit;
3994
4031
  onSessionId;
4032
+ emitChat;
3995
4033
  roles = /* @__PURE__ */ new Map();
3996
4034
  usage = { inputTokens: 0, outputTokens: 0, totalCostUsd: 0 };
3997
4035
  assistantText = "";
3998
4036
  /** Busy has been seen and idle has not — a turn is in flight on the bus. */
3999
4037
  active = false;
4000
4038
  latchedSessionId = null;
4039
+ relayedTextParts = /* @__PURE__ */ new Set();
4040
+ relayedToolUses = /* @__PURE__ */ new Set();
4041
+ relayedToolResults = /* @__PURE__ */ new Set();
4001
4042
  /** The opencode-assigned session id, once any event has carried it. */
4002
4043
  get sessionId() {
4003
4044
  return this.latchedSessionId;
@@ -4032,9 +4073,14 @@ var OpenCodeEventSource = class {
4032
4073
  const part = busPartOf(event);
4033
4074
  if (!part) return;
4034
4075
  if (!this.active) this.beginBusTurn();
4035
- if (typeof part.messageID !== "string" || this.roles.get(part.messageID) !== "assistant") {
4076
+ if (typeof part.messageID !== "string") return;
4077
+ const role = this.roles.get(part.messageID);
4078
+ if (role === "user") {
4079
+ this.relayTextPart(part, "user_text");
4036
4080
  return;
4037
4081
  }
4082
+ if (role !== "assistant") return;
4083
+ this.relayAssistantPart(part);
4038
4084
  accumulateUsage({ part }, this.usage);
4039
4085
  const mapped = mapOpenCodeEvent({ part });
4040
4086
  if (!mapped) return;
@@ -4048,6 +4094,9 @@ var OpenCodeEventSource = class {
4048
4094
  this.active = true;
4049
4095
  this.usage = { inputTokens: 0, outputTokens: 0, totalCostUsd: 0 };
4050
4096
  this.assistantText = "";
4097
+ this.relayedTextParts.clear();
4098
+ this.relayedToolUses.clear();
4099
+ this.relayedToolResults.clear();
4051
4100
  }
4052
4101
  /** Idle (or an error) closes the turn exactly once. */
4053
4102
  finishBusTurn(error) {
@@ -4056,6 +4105,56 @@ var OpenCodeEventSource = class {
4056
4105
  this.emit(
4057
4106
  error ? buildResultEvent(1, this.usage, "", "", error) : buildResultEvent(0, this.usage, this.assistantText.trim(), "")
4058
4107
  );
4108
+ this.emitChat?.({ kind: "turn_end" });
4109
+ }
4110
+ relayAssistantPart(part) {
4111
+ if (part.type === "text") {
4112
+ this.relayTextPart(part, "assistant_text");
4113
+ return;
4114
+ }
4115
+ if (part.type === "tool") this.relayToolPart(part);
4116
+ }
4117
+ relayToolPart(part) {
4118
+ const status = relayedToolStatus(part.state?.status);
4119
+ if (!status) return;
4120
+ const callId = truncate(partString(part, "callID") ?? partString(part, "id") ?? "", 100);
4121
+ const key = callId || `${part.messageID}:${partString(part, "tool") ?? "unknown"}`;
4122
+ this.relayToolUse(part, key, callId);
4123
+ if (status !== "running") this.relayToolResult(part, status, key, callId);
4124
+ }
4125
+ relayToolUse(part, key, callId) {
4126
+ if (this.relayedToolUses.has(key)) return;
4127
+ this.relayedToolUses.add(key);
4128
+ this.emitChat?.({
4129
+ kind: "tool_use",
4130
+ name: truncate(partString(part, "tool") ?? "unknown", 200),
4131
+ input: compactJson(part.state?.input),
4132
+ ...callId ? { id: callId } : {}
4133
+ });
4134
+ }
4135
+ relayToolResult(part, status, key, callId) {
4136
+ if (this.relayedToolResults.has(key)) return;
4137
+ this.relayedToolResults.add(key);
4138
+ this.emitChat?.({
4139
+ kind: "tool_result",
4140
+ ...callId ? { toolUseId: callId } : {},
4141
+ output: truncate(
4142
+ stringifyToolOutput(status === "error" ? part.state?.error : part.state?.output),
4143
+ CHAT_TOOL_OUTPUT_MAX
4144
+ ),
4145
+ isError: status === "error"
4146
+ });
4147
+ }
4148
+ relayTextPart(part, kind) {
4149
+ const text = typeof part.text === "string" ? part.text : "";
4150
+ if (text.trim() === "") return;
4151
+ const key = partString(part, "id") ?? `${part.messageID}:${kind}`;
4152
+ if (this.relayedTextParts.has(key)) return;
4153
+ this.relayedTextParts.add(key);
4154
+ this.emitChat?.({
4155
+ kind,
4156
+ text: truncate(kind === "user_text" ? text.trim() : text, CHAT_TEXT_MAX)
4157
+ });
4059
4158
  }
4060
4159
  latchSessionId(event) {
4061
4160
  if (this.latchedSessionId) return;
@@ -4492,14 +4591,14 @@ function stringField2(record, ...keys) {
4492
4591
  }
4493
4592
  return void 0;
4494
4593
  }
4495
- function truncate(text, max) {
4594
+ function truncate2(text, max) {
4496
4595
  return text.length > max ? `${text.slice(0, max)}\u2026` : text;
4497
4596
  }
4498
4597
  function compactQuestionsJson(questions) {
4499
4598
  const serialize = (qs) => JSON.stringify({ questions: qs });
4500
4599
  const withDescriptions = (max) => questions.map((q) => ({
4501
4600
  ...q,
4502
- options: q.options.map((o) => ({ ...o, description: truncate(o.description, max) }))
4601
+ options: q.options.map((o) => ({ ...o, description: truncate2(o.description, max) }))
4503
4602
  }));
4504
4603
  const full = serialize(questions);
4505
4604
  if (full.length <= QUESTION_INPUT_MAX) return full;
@@ -4559,7 +4658,7 @@ function mapAssistant2(record) {
4559
4658
  if (raw.type === "text") {
4560
4659
  const text = stringField2(raw, "text");
4561
4660
  if (text && text.length > 0) {
4562
- events.push({ kind: "assistant_text", text: truncate(text, TEXT_MAX) });
4661
+ events.push({ kind: "assistant_text", text: truncate2(text, TEXT_MAX) });
4563
4662
  }
4564
4663
  } else if (raw.type === "tool_use") {
4565
4664
  const name = stringField2(raw, "name");
@@ -4567,7 +4666,7 @@ function mapAssistant2(record) {
4567
4666
  const input = "input" in raw ? raw.input : void 0;
4568
4667
  const event = {
4569
4668
  kind: "tool_use",
4570
- name: truncate(name, 200),
4669
+ name: truncate2(name, 200),
4571
4670
  input: compactToolInput(name, input)
4572
4671
  };
4573
4672
  const id = stringField2(raw, "id");
@@ -4592,7 +4691,7 @@ function mapToolResults(content) {
4592
4691
  if (!isRecord3(raw) || raw.type !== "tool_result") continue;
4593
4692
  const event = {
4594
4693
  kind: "tool_result",
4595
- output: truncate(toolResultText(raw), TOOL_OUTPUT_MAX),
4694
+ output: truncate2(toolResultText(raw), TOOL_OUTPUT_MAX),
4596
4695
  isError: raw.is_error === true
4597
4696
  };
4598
4697
  const toolUseId = stringField2(raw, "tool_use_id");
@@ -4612,7 +4711,7 @@ function mapUser(record) {
4612
4711
  if (text === void 0) return [];
4613
4712
  const trimmed = text.trim();
4614
4713
  if (trimmed.length === 0 || isNonConversationText(trimmed)) return [];
4615
- return [{ kind: "user_text", text: truncate(trimmed, TEXT_MAX) }];
4714
+ return [{ kind: "user_text", text: truncate2(trimmed, TEXT_MAX) }];
4616
4715
  }
4617
4716
  function mapChatRecords(raw) {
4618
4717
  if (!isRecord3(raw)) return [];
@@ -5986,7 +6085,9 @@ var PtySession = class {
5986
6085
  session_id: id,
5987
6086
  model: this.options.model
5988
6087
  });
5989
- }
6088
+ this.sendChatEvent({ kind: "init", model: this.options.model, claudeSessionId: id });
6089
+ },
6090
+ (event) => this.sendChatEvent(event)
5990
6091
  );
5991
6092
  this.tailer = new JsonlTailer(
5992
6093
  eventsSinkPath,
@@ -9185,7 +9286,7 @@ Environment \u2014 already built and running. These are the facts you would othe
9185
9286
  `- The repo is cloned at your working directory with \`${context.githubBranch}\` checked out, dependencies installed, database migrated, git configured, and the dev stack up. Commit and push directly to this branch.`,
9186
9287
  `- The web app is served on port 3050, the API on port 7090.`,
9187
9288
  `- Web is served from a production build, not \`next dev\` \u2014 your edits do NOT hot-reload. Run \`bun run web:rebuild\` (rebuild + restart lands in ~2s) before re-testing a UI change. The API hot-reloads on its own.`,
9188
- `- Playwright MCP writes screenshots to the working directory, not a \`.playwright-mcp/\` subfolder. Move or delete them before committing.`,
9289
+ `- Browser automation is the Playwright CLI (\`playwright\`, pinned 1.62.1), NOT an MCP server \u2014 there are no \`mcp__playwright__*\` tools here. Only the headless shell is baked, so a launch must name it AND disable the sandbox: \`chromium.launch({ channel: "chromium-headless-shell", args: ["--no-sandbox"] })\`. A bare \`chromium.launch()\` FAILS: since playwright 1.49 that resolves to the full browser, which is deliberately not installed (pods have no display and cannot run Chromium's sandbox). Screenshots land wherever you write them \u2014 move or delete them before committing.`,
9189
9290
  `- The clone is \`--single-branch\`, so a bare \`git fetch origin <branch>\` does NOT create \`origin/<branch>\`. To reference any other branch, use the explicit refspec: \`git fetch origin <branch>:refs/remotes/origin/<branch>\`.`,
9190
9291
  `- The shell cwd resets between Bash calls, and so does every shell variable. A var you export in one call is EMPTY in the next, which silently redirects output to \`/\` and loses it. Write literal absolute paths (\`git -C\`, \`bun run --cwd\`), and \`mkdir -p\` a directory in the SAME call as the redirect that writes to it.`,
9191
9292
  `- The \`gh\` CLI is available for READ-ONLY PR and CI state (\`gh pr view\`, \`gh pr checks\`, \`gh pr diff\`). Use the mcp__conveyor__* tools for anything that mutates a PR or card.`,
@@ -9754,6 +9855,7 @@ function defineToolContract(contract) {
9754
9855
  }
9755
9856
  var mcpProjectId = f.optional(f.string({ desc: "Target Conveyor project ID" }));
9756
9857
  var cardDescriptionDesc = (lead) => `${lead} \u2014 ${CARD_DESCRIPTION_FIELD_HINT}`;
9858
+ var storyPointValueDesc = "Story point value (1=Common, 2=Magic, 3=Rare, 5=Unique)";
9757
9859
  var getTaskContract = defineToolContract({
9758
9860
  name: "get_task",
9759
9861
  agent: {
@@ -10170,7 +10272,7 @@ var dependenciesContracts = [
10170
10272
  addDependencyContract,
10171
10273
  removeDependencyContract
10172
10274
  ];
10173
- var SP_DESCRIPTION = "Story point value (1=Common, 2=Magic, 3=Rare, 5=Unique)";
10275
+ var SP_DESCRIPTION = storyPointValueDesc;
10174
10276
  var AGENT_FOLLOW_PARENT_STATUS = "Child mirrors the parent task's status automatically \u2014 for subtasks that ship on the parent's branch/PR with no build or PR of their own. Manual status writes on a follower stick only until the parent's next transition.";
10175
10277
  var MCP_FOLLOW_PARENT_STATUS = "When true, this subtask mirrors the parent task's status automatically \u2014 for children that ship on the parent's branch/PR and have no build or PR of their own. Manual status writes on a follower stick only until the parent's next transition.";
10176
10278
  var AGENT_DEPENDS_ON = "Sibling subtask ids or slugs this subtask blocks on (it won't start until they merge to dev). Set explicit dependency metadata here instead of describing order in the plan text \u2014 the pack runner schedules children off these edges. Omit / leave empty for independent children so they run in parallel.";
@@ -16206,4 +16308,4 @@ export {
16206
16308
  loadConveyorConfig,
16207
16309
  unshallowRepo
16208
16310
  };
16209
- //# sourceMappingURL=chunk-FCNMY7NL.js.map
16311
+ //# sourceMappingURL=chunk-PG3Z6RIC.js.map