@rallycry/conveyor-agent 10.13.53 → 10.13.55

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.
@@ -2680,15 +2680,18 @@ var UpdateProjectTaskRequestSchema = z5.object({
2680
2680
  // Canonical risk level, or null to clear. Resolved to the project's
2681
2681
  // configured Risk row (by rank) in the handler.
2682
2682
  risk: riskLevelSchema.nullable().optional(),
2683
+ // Story-point value, or null to clear. Resolved to the project's configured
2684
+ // StoryPoint row in the handler, which rejects an unconfigured value.
2685
+ storyPointValue: z5.number().int().positive().nullable().optional(),
2683
2686
  assignedUserId: z5.string().nullish(),
2684
2687
  // Move to a different sub-project board, or null to move to the parent board.
2685
2688
  // Validated to belong to `projectId` in the handler.
2686
2689
  subProjectId: z5.string().nullable().optional(),
2687
2690
  requestingUserId: z5.string().optional()
2688
2691
  }).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,
2692
+ (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
2693
  {
2691
- message: "update_task requires at least one field to change (title, description, plan, status, risk, assignedUserId, or subProjectId)"
2694
+ message: "update_task requires at least one field to change (title, description, plan, status, risk, storyPointValue, assignedUserId, or subProjectId)"
2692
2695
  }
2693
2696
  );
2694
2697
  var TransitionProjectTaskStatusRequestSchema = z5.object({
@@ -3934,6 +3937,15 @@ function mapToolPart(part) {
3934
3937
  }
3935
3938
  };
3936
3939
  }
3940
+ function stringifyToolOutput(output) {
3941
+ if (output === void 0 || output === null) return "";
3942
+ if (typeof output === "string") return output;
3943
+ try {
3944
+ return JSON.stringify(output);
3945
+ } catch {
3946
+ return String(output);
3947
+ }
3948
+ }
3937
3949
  function errorMessageOf(event) {
3938
3950
  if (event.type !== "error") return null;
3939
3951
  const error = event.error;
@@ -3985,19 +3997,44 @@ ${stderrTail.trim()}`);
3985
3997
 
3986
3998
  // src/harness/opencode/event-source.ts
3987
3999
  var MAX_TRACKED_MESSAGES = 200;
4000
+ var CHAT_TEXT_MAX = 16e3;
4001
+ var CHAT_TOOL_INPUT_MAX = 1900;
4002
+ var CHAT_TOOL_OUTPUT_MAX = 1900;
4003
+ function truncate(text, max) {
4004
+ return text.length > max ? `${text.slice(0, max)}\u2026` : text;
4005
+ }
4006
+ function compactJson(value) {
4007
+ try {
4008
+ return JSON.stringify(value ?? {}).slice(0, CHAT_TOOL_INPUT_MAX);
4009
+ } catch {
4010
+ return String(value).slice(0, CHAT_TOOL_INPUT_MAX);
4011
+ }
4012
+ }
4013
+ function partString(part, key) {
4014
+ const value = part[key];
4015
+ return typeof value === "string" && value.length > 0 ? value : null;
4016
+ }
4017
+ function relayedToolStatus(status) {
4018
+ return status === "running" || status === "completed" || status === "error" ? status : null;
4019
+ }
3988
4020
  var OpenCodeEventSource = class {
3989
- constructor(emit, onSessionId) {
4021
+ constructor(emit, onSessionId, emitChat) {
3990
4022
  this.emit = emit;
3991
4023
  this.onSessionId = onSessionId;
4024
+ this.emitChat = emitChat;
3992
4025
  }
3993
4026
  emit;
3994
4027
  onSessionId;
4028
+ emitChat;
3995
4029
  roles = /* @__PURE__ */ new Map();
3996
4030
  usage = { inputTokens: 0, outputTokens: 0, totalCostUsd: 0 };
3997
4031
  assistantText = "";
3998
4032
  /** Busy has been seen and idle has not — a turn is in flight on the bus. */
3999
4033
  active = false;
4000
4034
  latchedSessionId = null;
4035
+ relayedTextParts = /* @__PURE__ */ new Set();
4036
+ relayedToolUses = /* @__PURE__ */ new Set();
4037
+ relayedToolResults = /* @__PURE__ */ new Set();
4001
4038
  /** The opencode-assigned session id, once any event has carried it. */
4002
4039
  get sessionId() {
4003
4040
  return this.latchedSessionId;
@@ -4032,9 +4069,14 @@ var OpenCodeEventSource = class {
4032
4069
  const part = busPartOf(event);
4033
4070
  if (!part) return;
4034
4071
  if (!this.active) this.beginBusTurn();
4035
- if (typeof part.messageID !== "string" || this.roles.get(part.messageID) !== "assistant") {
4072
+ if (typeof part.messageID !== "string") return;
4073
+ const role = this.roles.get(part.messageID);
4074
+ if (role === "user") {
4075
+ this.relayTextPart(part, "user_text");
4036
4076
  return;
4037
4077
  }
4078
+ if (role !== "assistant") return;
4079
+ this.relayAssistantPart(part);
4038
4080
  accumulateUsage({ part }, this.usage);
4039
4081
  const mapped = mapOpenCodeEvent({ part });
4040
4082
  if (!mapped) return;
@@ -4048,6 +4090,9 @@ var OpenCodeEventSource = class {
4048
4090
  this.active = true;
4049
4091
  this.usage = { inputTokens: 0, outputTokens: 0, totalCostUsd: 0 };
4050
4092
  this.assistantText = "";
4093
+ this.relayedTextParts.clear();
4094
+ this.relayedToolUses.clear();
4095
+ this.relayedToolResults.clear();
4051
4096
  }
4052
4097
  /** Idle (or an error) closes the turn exactly once. */
4053
4098
  finishBusTurn(error) {
@@ -4056,6 +4101,56 @@ var OpenCodeEventSource = class {
4056
4101
  this.emit(
4057
4102
  error ? buildResultEvent(1, this.usage, "", "", error) : buildResultEvent(0, this.usage, this.assistantText.trim(), "")
4058
4103
  );
4104
+ this.emitChat?.({ kind: "turn_end" });
4105
+ }
4106
+ relayAssistantPart(part) {
4107
+ if (part.type === "text") {
4108
+ this.relayTextPart(part, "assistant_text");
4109
+ return;
4110
+ }
4111
+ if (part.type === "tool") this.relayToolPart(part);
4112
+ }
4113
+ relayToolPart(part) {
4114
+ const status = relayedToolStatus(part.state?.status);
4115
+ if (!status) return;
4116
+ const callId = truncate(partString(part, "callID") ?? partString(part, "id") ?? "", 100);
4117
+ const key = callId || `${part.messageID}:${partString(part, "tool") ?? "unknown"}`;
4118
+ this.relayToolUse(part, key, callId);
4119
+ if (status !== "running") this.relayToolResult(part, status, key, callId);
4120
+ }
4121
+ relayToolUse(part, key, callId) {
4122
+ if (this.relayedToolUses.has(key)) return;
4123
+ this.relayedToolUses.add(key);
4124
+ this.emitChat?.({
4125
+ kind: "tool_use",
4126
+ name: truncate(partString(part, "tool") ?? "unknown", 200),
4127
+ input: compactJson(part.state?.input),
4128
+ ...callId ? { id: callId } : {}
4129
+ });
4130
+ }
4131
+ relayToolResult(part, status, key, callId) {
4132
+ if (this.relayedToolResults.has(key)) return;
4133
+ this.relayedToolResults.add(key);
4134
+ this.emitChat?.({
4135
+ kind: "tool_result",
4136
+ ...callId ? { toolUseId: callId } : {},
4137
+ output: truncate(
4138
+ stringifyToolOutput(status === "error" ? part.state?.error : part.state?.output),
4139
+ CHAT_TOOL_OUTPUT_MAX
4140
+ ),
4141
+ isError: status === "error"
4142
+ });
4143
+ }
4144
+ relayTextPart(part, kind) {
4145
+ const text = typeof part.text === "string" ? part.text : "";
4146
+ if (text.trim() === "") return;
4147
+ const key = partString(part, "id") ?? `${part.messageID}:${kind}`;
4148
+ if (this.relayedTextParts.has(key)) return;
4149
+ this.relayedTextParts.add(key);
4150
+ this.emitChat?.({
4151
+ kind,
4152
+ text: truncate(kind === "user_text" ? text.trim() : text, CHAT_TEXT_MAX)
4153
+ });
4059
4154
  }
4060
4155
  latchSessionId(event) {
4061
4156
  if (this.latchedSessionId) return;
@@ -4492,14 +4587,14 @@ function stringField2(record, ...keys) {
4492
4587
  }
4493
4588
  return void 0;
4494
4589
  }
4495
- function truncate(text, max) {
4590
+ function truncate2(text, max) {
4496
4591
  return text.length > max ? `${text.slice(0, max)}\u2026` : text;
4497
4592
  }
4498
4593
  function compactQuestionsJson(questions) {
4499
4594
  const serialize = (qs) => JSON.stringify({ questions: qs });
4500
4595
  const withDescriptions = (max) => questions.map((q) => ({
4501
4596
  ...q,
4502
- options: q.options.map((o) => ({ ...o, description: truncate(o.description, max) }))
4597
+ options: q.options.map((o) => ({ ...o, description: truncate2(o.description, max) }))
4503
4598
  }));
4504
4599
  const full = serialize(questions);
4505
4600
  if (full.length <= QUESTION_INPUT_MAX) return full;
@@ -4559,7 +4654,7 @@ function mapAssistant2(record) {
4559
4654
  if (raw.type === "text") {
4560
4655
  const text = stringField2(raw, "text");
4561
4656
  if (text && text.length > 0) {
4562
- events.push({ kind: "assistant_text", text: truncate(text, TEXT_MAX) });
4657
+ events.push({ kind: "assistant_text", text: truncate2(text, TEXT_MAX) });
4563
4658
  }
4564
4659
  } else if (raw.type === "tool_use") {
4565
4660
  const name = stringField2(raw, "name");
@@ -4567,7 +4662,7 @@ function mapAssistant2(record) {
4567
4662
  const input = "input" in raw ? raw.input : void 0;
4568
4663
  const event = {
4569
4664
  kind: "tool_use",
4570
- name: truncate(name, 200),
4665
+ name: truncate2(name, 200),
4571
4666
  input: compactToolInput(name, input)
4572
4667
  };
4573
4668
  const id = stringField2(raw, "id");
@@ -4592,7 +4687,7 @@ function mapToolResults(content) {
4592
4687
  if (!isRecord3(raw) || raw.type !== "tool_result") continue;
4593
4688
  const event = {
4594
4689
  kind: "tool_result",
4595
- output: truncate(toolResultText(raw), TOOL_OUTPUT_MAX),
4690
+ output: truncate2(toolResultText(raw), TOOL_OUTPUT_MAX),
4596
4691
  isError: raw.is_error === true
4597
4692
  };
4598
4693
  const toolUseId = stringField2(raw, "tool_use_id");
@@ -4612,7 +4707,7 @@ function mapUser(record) {
4612
4707
  if (text === void 0) return [];
4613
4708
  const trimmed = text.trim();
4614
4709
  if (trimmed.length === 0 || isNonConversationText(trimmed)) return [];
4615
- return [{ kind: "user_text", text: truncate(trimmed, TEXT_MAX) }];
4710
+ return [{ kind: "user_text", text: truncate2(trimmed, TEXT_MAX) }];
4616
4711
  }
4617
4712
  function mapChatRecords(raw) {
4618
4713
  if (!isRecord3(raw)) return [];
@@ -5986,7 +6081,9 @@ var PtySession = class {
5986
6081
  session_id: id,
5987
6082
  model: this.options.model
5988
6083
  });
5989
- }
6084
+ this.sendChatEvent({ kind: "init", model: this.options.model, claudeSessionId: id });
6085
+ },
6086
+ (event) => this.sendChatEvent(event)
5990
6087
  );
5991
6088
  this.tailer = new JsonlTailer(
5992
6089
  eventsSinkPath,
@@ -6694,11 +6791,26 @@ var PtyHarness = class _PtyHarness {
6694
6791
  bridge;
6695
6792
  adapter;
6696
6793
  static log = createServiceLogger("pty-harness");
6697
- /** Delegated to the adapter: Claude tails a transcript + hook socket, while a
6698
- * raw-relay TUI (opencode) has no trusted event source at all. */
6794
+ /** Delegated to the adapter: Claude tails a transcript + hook socket, opencode
6795
+ * tails its plugin sink, and a raw-relay TUI has no trusted event source. */
6699
6796
  get emitsStructuredEvents() {
6700
6797
  return this.adapter.capabilities.structuredEvents;
6701
6798
  }
6799
+ /**
6800
+ * Does this adapter own the shared `~/.claude` config home (credentials,
6801
+ * transcripts, onboarding flags)? Claude alone does — opencode keeps its
6802
+ * state under `~/.local/share/opencode` and its event sink in the session
6803
+ * tempDir.
6804
+ *
6805
+ * Gate every `~/.claude` probe on this, NOT on
6806
+ * `capabilities.structuredEvents`. That flag meant "is this Claude" only by
6807
+ * accident — Claude was the sole adapter that had it. Once the opencode
6808
+ * adapter gained structured events via the plugin sink, the proxy silently
6809
+ * inverted and opencode cards began running Claude-only probes.
6810
+ */
6811
+ get ownsClaudeConfigHome() {
6812
+ return this.adapter.id === "claude-code";
6813
+ }
6702
6814
  /** Fingerprint of the spawn-time options a reused process cannot change. */
6703
6815
  fingerprintOf(options) {
6704
6816
  return this.adapter.spawnFingerprint({
@@ -6790,7 +6902,7 @@ var PtyHarness = class _PtyHarness {
6790
6902
  * so here it just declines to force a respawn.
6791
6903
  */
6792
6904
  async parkedHomeDied(options) {
6793
- if (!this.adapter.capabilities.structuredEvents) return false;
6905
+ if (!this.ownsClaudeConfigHome) return false;
6794
6906
  try {
6795
6907
  const { fellBack } = await ensureUsableClaudeConfigHome(options.cwd, _PtyHarness.log);
6796
6908
  if (!fellBack) return false;
@@ -6812,11 +6924,11 @@ var PtyHarness = class _PtyHarness {
6812
6924
  */
6813
6925
  async spawnSession(prompt, options, want) {
6814
6926
  const session = new PtySession(prompt, options, want, this.bridge, this.adapter);
6815
- if (this.adapter.capabilities.structuredEvents) {
6927
+ if (this.ownsClaudeConfigHome) {
6816
6928
  await ensureUsableClaudeConfigHome(options.cwd, _PtyHarness.log);
6817
6929
  }
6818
6930
  await this.adapter.prepareEnvironment({ cwd: options.cwd });
6819
- if (this.adapter.capabilities.structuredEvents) {
6931
+ if (this.ownsClaudeConfigHome) {
6820
6932
  await this.warnIfAuthNotReady();
6821
6933
  }
6822
6934
  session.onExit(() => this.handleSessionExit(session));
@@ -9739,6 +9851,7 @@ function defineToolContract(contract) {
9739
9851
  }
9740
9852
  var mcpProjectId = f.optional(f.string({ desc: "Target Conveyor project ID" }));
9741
9853
  var cardDescriptionDesc = (lead) => `${lead} \u2014 ${CARD_DESCRIPTION_FIELD_HINT}`;
9854
+ var storyPointValueDesc = "Story point value (1=Common, 2=Magic, 3=Rare, 5=Unique)";
9742
9855
  var getTaskContract = defineToolContract({
9743
9856
  name: "get_task",
9744
9857
  agent: {
@@ -10155,7 +10268,7 @@ var dependenciesContracts = [
10155
10268
  addDependencyContract,
10156
10269
  removeDependencyContract
10157
10270
  ];
10158
- var SP_DESCRIPTION = "Story point value (1=Common, 2=Magic, 3=Rare, 5=Unique)";
10271
+ var SP_DESCRIPTION = storyPointValueDesc;
10159
10272
  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.";
10160
10273
  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.";
10161
10274
  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.";
@@ -16191,4 +16304,4 @@ export {
16191
16304
  loadConveyorConfig,
16192
16305
  unshallowRepo
16193
16306
  };
16194
- //# sourceMappingURL=chunk-HSL7QH72.js.map
16307
+ //# sourceMappingURL=chunk-V33EYJWC.js.map