@rallycry/conveyor-agent 10.13.8 → 10.13.9

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -2318,7 +2318,10 @@ var UpdateTaskPropertiesRequestSchema = z3.object({
2318
2318
  tagIds: z3.array(z3.string()).optional(),
2319
2319
  tagNames: z3.array(z3.string()).optional(),
2320
2320
  githubPRUrl: z3.string().url().optional(),
2321
- githubBranch: z3.string().optional()
2321
+ githubBranch: z3.string().optional(),
2322
+ // Canonical risk level, or null to clear — same semantics as the headless
2323
+ // update_task boundary (resolved to the project's Risk row in the handler).
2324
+ risk: riskLevelSchema.nullable().optional()
2322
2325
  });
2323
2326
  var ListIconsRequestSchema = z3.object({
2324
2327
  sessionId: z3.string()
@@ -2650,7 +2653,13 @@ var UpdateProjectTaskRequestSchema = z4.object({
2650
2653
  projectId: z4.string(),
2651
2654
  taskId: z4.string(),
2652
2655
  title: z4.string().optional(),
2656
+ description: z4.string().optional(),
2653
2657
  plan: z4.string().optional(),
2658
+ // Enum validation lives at the MCP tool layer (mirrors createProjectTask);
2659
+ // the handler routes through the shared updateStatus core (InProgress
2660
+ // dependency check + cleanup/board/Slack side effects), not the stricter
2661
+ // card-type-validating path the Socket.IO updateTaskStatus mutation uses.
2662
+ status: z4.string().optional(),
2654
2663
  // Canonical risk level, or null to clear. Resolved to the project's
2655
2664
  // configured Risk row (by rank) in the handler.
2656
2665
  risk: riskLevelSchema.nullable().optional(),
@@ -2660,9 +2669,9 @@ var UpdateProjectTaskRequestSchema = z4.object({
2660
2669
  subProjectId: z4.string().nullable().optional(),
2661
2670
  requestingUserId: z4.string().optional()
2662
2671
  }).strict().refine(
2663
- (v) => v.title !== void 0 || v.plan !== void 0 || v.risk !== void 0 || v.assignedUserId !== void 0 || v.subProjectId !== void 0,
2672
+ (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,
2664
2673
  {
2665
- message: "update_task requires at least one field to change (title, plan, risk, assignedUserId, or subProjectId)"
2674
+ message: "update_task requires at least one field to change (title, description, plan, status, risk, assignedUserId, or subProjectId)"
2666
2675
  }
2667
2676
  );
2668
2677
  var TransitionProjectTaskStatusRequestSchema = z4.object({
@@ -2814,6 +2823,9 @@ var UpdateProjectSubtaskRequestSchema = z4.object({
2814
2823
  ordinal: z4.number().int().nonnegative().optional(),
2815
2824
  storyPointValue: z4.number().int().positive().optional(),
2816
2825
  followParentStatus: z4.boolean().optional(),
2826
+ /** Replace-set of sibling subtask ids/slugs this subtask blocks on ([] clears).
2827
+ * Mirrors the in-pod updateSubtask semantics. */
2828
+ dependsOn: z4.array(z4.string().min(1)).max(32).optional(),
2817
2829
  requestingUserId: z4.string().optional()
2818
2830
  });
2819
2831
  var DeleteProjectSubtaskRequestSchema = z4.object({
@@ -3389,6 +3401,7 @@ var ClaudeCodeHarness = class {
3389
3401
  };
3390
3402
 
3391
3403
  // src/harness/pty/session.ts
3404
+ import { randomUUID as randomUUID2 } from "crypto";
3392
3405
  import { mkdtemp as mkdtemp2, mkdir as mkdir3, rm as rm5 } from "fs/promises";
3393
3406
  import { tmpdir as tmpdir3 } from "os";
3394
3407
  import { join as join4, dirname } from "path";
@@ -3787,11 +3800,131 @@ function matchUsageLimitBanner(text, now = Date.now()) {
3787
3800
  };
3788
3801
  }
3789
3802
 
3803
+ // src/harness/pty/pty-support.ts
3804
+ import { stat as stat2 } from "fs/promises";
3805
+ var MAX_DIAGNOSTIC_OUTPUT = 4e3;
3806
+ var MAX_BETWEEN_TURN_BUFFER = 500;
3807
+ var SUBMIT_SETTLE_MS = 300;
3808
+ var SUBMIT_NUDGE_INTERVAL_MS = 2e3;
3809
+ var SUBMIT_NUDGE_MAX_PRESSES = 5;
3810
+ var SUBMIT_NUDGE_SLOW_INTERVAL_MS = 5e3;
3811
+ var SUBMIT_NUDGE_WINDOW_MS = 9e4;
3812
+ var PLAN_DIALOG_FIRST_PRESS_MS = 700;
3813
+ var PLAN_DIALOG_INTERVAL_MS = 1500;
3814
+ var PLAN_DIALOG_SLOW_INTERVAL_MS = 5e3;
3815
+ var PLAN_DIALOG_FAST_WINDOW_MS = 1e4;
3816
+ var PLAN_DIALOG_WINDOW_MS = 9e4;
3817
+ function envMs(name, fallback) {
3818
+ const raw = Number(process.env[name]);
3819
+ return Number.isFinite(raw) && raw > 0 ? raw : fallback;
3820
+ }
3821
+ function resolveSubmitSettleMs() {
3822
+ return envMs("CONVEYOR_PTY_SUBMIT_SETTLE_MS", SUBMIT_SETTLE_MS);
3823
+ }
3824
+ function resolveSubmitNudgeTiming() {
3825
+ return {
3826
+ intervalMs: envMs("CONVEYOR_PTY_NUDGE_INTERVAL_MS", SUBMIT_NUDGE_INTERVAL_MS),
3827
+ slowIntervalMs: envMs("CONVEYOR_PTY_NUDGE_SLOW_INTERVAL_MS", SUBMIT_NUDGE_SLOW_INTERVAL_MS),
3828
+ maxPresses: SUBMIT_NUDGE_MAX_PRESSES,
3829
+ windowMs: envMs("CONVEYOR_PTY_NUDGE_WINDOW_MS", SUBMIT_NUDGE_WINDOW_MS)
3830
+ };
3831
+ }
3832
+ function resolvePlanDialogTiming() {
3833
+ return {
3834
+ firstPressMs: envMs("CONVEYOR_PTY_PLAN_DIALOG_FIRST_PRESS_MS", PLAN_DIALOG_FIRST_PRESS_MS),
3835
+ intervalMs: envMs("CONVEYOR_PTY_PLAN_DIALOG_INTERVAL_MS", PLAN_DIALOG_INTERVAL_MS),
3836
+ slowIntervalMs: envMs(
3837
+ "CONVEYOR_PTY_PLAN_DIALOG_SLOW_INTERVAL_MS",
3838
+ PLAN_DIALOG_SLOW_INTERVAL_MS
3839
+ ),
3840
+ fastWindowMs: envMs("CONVEYOR_PTY_PLAN_DIALOG_FAST_WINDOW_MS", PLAN_DIALOG_FAST_WINDOW_MS),
3841
+ windowMs: envMs("CONVEYOR_PTY_PLAN_DIALOG_WINDOW_MS", PLAN_DIALOG_WINDOW_MS)
3842
+ };
3843
+ }
3844
+ function turnOptionsFrom(options) {
3845
+ return {
3846
+ canUseTool: options.canUseTool,
3847
+ promptDelivery: options.promptDelivery,
3848
+ planDialogAutoAccept: options.planDialogAutoAccept,
3849
+ abortController: options.abortController
3850
+ };
3851
+ }
3852
+ function isRecord2(value) {
3853
+ return typeof value === "object" && value !== null;
3854
+ }
3855
+ function extractSpawn(mod) {
3856
+ if (!isRecord2(mod)) return null;
3857
+ if (typeof mod.spawn === "function") return mod.spawn;
3858
+ const def = mod.default;
3859
+ if (isRecord2(def) && typeof def.spawn === "function") return def.spawn;
3860
+ return null;
3861
+ }
3862
+ async function loadPtySpawn() {
3863
+ const mod = await import("node-pty");
3864
+ const spawn2 = extractSpawn(mod);
3865
+ if (!spawn2) throw new Error("node-pty: spawn export not found");
3866
+ return spawn2;
3867
+ }
3868
+ function inheritedEnv(socketPath) {
3869
+ const env = {};
3870
+ for (const [key, value] of Object.entries(process.env)) {
3871
+ if (typeof value === "string") env[key] = value;
3872
+ }
3873
+ if (env.CLAUDE_CODE_OAUTH_TOKEN) {
3874
+ delete env.ANTHROPIC_API_KEY;
3875
+ }
3876
+ if (socketPath) {
3877
+ env.CONVEYOR_HOOK_SOCKET = socketPath;
3878
+ }
3879
+ env.MCP_TIMEOUT ??= "60000";
3880
+ env.MCP_TOOL_TIMEOUT ??= "180000";
3881
+ return env;
3882
+ }
3883
+ function buildPromptBytes(text) {
3884
+ return `\x1B[200~${text}\x1B[201~`;
3885
+ }
3886
+ function renderPromptContentText(content) {
3887
+ return content.map((block) => {
3888
+ const b = block;
3889
+ if (b?.type === "text" && typeof b.text === "string") return b.text;
3890
+ if (b?.type === "image") {
3891
+ return `[Image attachment \u2014 use list_task_files / get_attachment to view]`;
3892
+ }
3893
+ return JSON.stringify(block);
3894
+ }).join("\n\n");
3895
+ }
3896
+ async function transcriptSize(path4) {
3897
+ try {
3898
+ return (await stat2(path4)).size;
3899
+ } catch {
3900
+ return 0;
3901
+ }
3902
+ }
3903
+ function parseUserQuestions(input) {
3904
+ if (!Array.isArray(input.questions)) return [];
3905
+ const questions = [];
3906
+ for (const entry of input.questions) {
3907
+ if (!isRecord2(entry)) continue;
3908
+ if (typeof entry.question !== "string") continue;
3909
+ const options = Array.isArray(entry.options) ? entry.options.filter(isRecord2).filter((o) => typeof o.label === "string").map((o) => ({
3910
+ label: o.label,
3911
+ description: typeof o.description === "string" ? o.description : ""
3912
+ })) : [];
3913
+ questions.push({
3914
+ question: entry.question,
3915
+ header: typeof entry.header === "string" ? entry.header : "",
3916
+ options,
3917
+ ...typeof entry.multiSelect === "boolean" ? { multiSelect: entry.multiSelect } : {}
3918
+ });
3919
+ }
3920
+ return questions;
3921
+ }
3922
+
3790
3923
  // src/harness/pty/chat-record-mapper.ts
3791
3924
  var TEXT_MAX = 16e3;
3792
3925
  var TOOL_INPUT_MAX = 1900;
3793
3926
  var TOOL_OUTPUT_MAX = 1900;
3794
- function isRecord2(value) {
3927
+ function isRecord3(value) {
3795
3928
  return typeof value === "object" && value !== null;
3796
3929
  }
3797
3930
  function isUnknownArray2(value) {
@@ -3807,6 +3940,27 @@ function stringField2(record, ...keys) {
3807
3940
  function truncate(text, max) {
3808
3941
  return text.length > max ? `${text.slice(0, max)}\u2026` : text;
3809
3942
  }
3943
+ function compactQuestionsJson(questions) {
3944
+ const serialize = (qs) => JSON.stringify({ questions: qs });
3945
+ const withDescriptions = (max) => questions.map((q) => ({
3946
+ ...q,
3947
+ options: q.options.map((o) => ({ ...o, description: truncate(o.description, max) }))
3948
+ }));
3949
+ const full = serialize(questions);
3950
+ if (full.length <= TOOL_INPUT_MAX) return full;
3951
+ const shortened = serialize(withDescriptions(80));
3952
+ if (shortened.length <= TOOL_INPUT_MAX) return shortened;
3953
+ const bare = serialize(withDescriptions(0));
3954
+ if (bare.length <= TOOL_INPUT_MAX) return bare;
3955
+ return bare.slice(0, TOOL_INPUT_MAX);
3956
+ }
3957
+ function compactToolInput(name, input) {
3958
+ if (name === "AskUserQuestion" && isRecord3(input)) {
3959
+ const questions = parseUserQuestions(input);
3960
+ if (questions.length > 0) return compactQuestionsJson(questions);
3961
+ }
3962
+ return JSON.stringify(input ?? {}).slice(0, TOOL_INPUT_MAX);
3963
+ }
3810
3964
  function isNonConversationText(text) {
3811
3965
  const trimmed = text.trimStart();
3812
3966
  return trimmed.startsWith("<command-name>") || trimmed.startsWith("<local-command-") || trimmed.startsWith("<task-notification>");
@@ -3826,11 +3980,11 @@ function mapSystem2(record) {
3826
3980
  }
3827
3981
  function mapAssistant2(record) {
3828
3982
  const message = record.message;
3829
- if (!isRecord2(message)) return [];
3983
+ if (!isRecord3(message)) return [];
3830
3984
  const content = isUnknownArray2(message.content) ? message.content : [];
3831
3985
  const events = [];
3832
3986
  for (const raw of content) {
3833
- if (!isRecord2(raw)) continue;
3987
+ if (!isRecord3(raw)) continue;
3834
3988
  if (raw.type === "text") {
3835
3989
  const text = stringField2(raw, "text");
3836
3990
  if (text && text.length > 0) {
@@ -3843,7 +3997,7 @@ function mapAssistant2(record) {
3843
3997
  const event = {
3844
3998
  kind: "tool_use",
3845
3999
  name: truncate(name, 200),
3846
- input: JSON.stringify(input ?? {}).slice(0, TOOL_INPUT_MAX)
4000
+ input: compactToolInput(name, input)
3847
4001
  };
3848
4002
  const id = stringField2(raw, "id");
3849
4003
  if (id !== void 0) event.id = id;
@@ -3857,14 +4011,14 @@ function toolResultText(block) {
3857
4011
  const content = block.content;
3858
4012
  if (typeof content === "string") return content;
3859
4013
  if (isUnknownArray2(content)) {
3860
- return content.filter((b) => isRecord2(b) && b.type === "text").map((b) => typeof b.text === "string" ? b.text : "").filter((t) => t.length > 0).join("\n");
4014
+ return content.filter((b) => isRecord3(b) && b.type === "text").map((b) => typeof b.text === "string" ? b.text : "").filter((t) => t.length > 0).join("\n");
3861
4015
  }
3862
4016
  return "";
3863
4017
  }
3864
4018
  function mapToolResults(content) {
3865
4019
  const events = [];
3866
4020
  for (const raw of content) {
3867
- if (!isRecord2(raw) || raw.type !== "tool_result") continue;
4021
+ if (!isRecord3(raw) || raw.type !== "tool_result") continue;
3868
4022
  const event = {
3869
4023
  kind: "tool_result",
3870
4024
  output: truncate(toolResultText(raw), TOOL_OUTPUT_MAX),
@@ -3878,15 +4032,15 @@ function mapToolResults(content) {
3878
4032
  }
3879
4033
  function mapUser(record) {
3880
4034
  const message = record.message;
3881
- if (!isRecord2(message)) return [];
4035
+ if (!isRecord3(message)) return [];
3882
4036
  const content = message.content;
3883
4037
  let text;
3884
4038
  if (typeof content === "string") {
3885
4039
  text = content;
3886
4040
  } else if (isUnknownArray2(content)) {
3887
- const hasToolResult = content.some((b) => isRecord2(b) && b.type === "tool_result");
4041
+ const hasToolResult = content.some((b) => isRecord3(b) && b.type === "tool_result");
3888
4042
  if (hasToolResult) return mapToolResults(content);
3889
- text = content.filter((b) => isRecord2(b) && b.type === "text").map((b) => typeof b.text === "string" ? b.text : "").filter((t) => t.length > 0).join("\n");
4043
+ text = content.filter((b) => isRecord3(b) && b.type === "text").map((b) => typeof b.text === "string" ? b.text : "").filter((t) => t.length > 0).join("\n");
3890
4044
  } else {
3891
4045
  return [];
3892
4046
  }
@@ -3895,7 +4049,7 @@ function mapUser(record) {
3895
4049
  return [{ kind: "user_text", text: truncate(trimmed, TEXT_MAX) }];
3896
4050
  }
3897
4051
  function mapChatRecords(raw) {
3898
- if (!isRecord2(raw)) return [];
4052
+ if (!isRecord3(raw)) return [];
3899
4053
  if (raw.isSidechain === true || raw.isMeta === true) return [];
3900
4054
  switch (raw.type) {
3901
4055
  case "system":
@@ -4799,126 +4953,6 @@ async function removeConveyorCredentials(env = process.env) {
4799
4953
  }
4800
4954
  }
4801
4955
 
4802
- // src/harness/pty/pty-support.ts
4803
- import { stat as stat2 } from "fs/promises";
4804
- var MAX_DIAGNOSTIC_OUTPUT = 4e3;
4805
- var MAX_BETWEEN_TURN_BUFFER = 500;
4806
- var SUBMIT_SETTLE_MS = 300;
4807
- var SUBMIT_NUDGE_INTERVAL_MS = 2e3;
4808
- var SUBMIT_NUDGE_MAX_PRESSES = 5;
4809
- var SUBMIT_NUDGE_SLOW_INTERVAL_MS = 5e3;
4810
- var SUBMIT_NUDGE_WINDOW_MS = 9e4;
4811
- var PLAN_DIALOG_FIRST_PRESS_MS = 700;
4812
- var PLAN_DIALOG_INTERVAL_MS = 1500;
4813
- var PLAN_DIALOG_SLOW_INTERVAL_MS = 5e3;
4814
- var PLAN_DIALOG_FAST_WINDOW_MS = 1e4;
4815
- var PLAN_DIALOG_WINDOW_MS = 9e4;
4816
- function envMs(name, fallback) {
4817
- const raw = Number(process.env[name]);
4818
- return Number.isFinite(raw) && raw > 0 ? raw : fallback;
4819
- }
4820
- function resolveSubmitSettleMs() {
4821
- return envMs("CONVEYOR_PTY_SUBMIT_SETTLE_MS", SUBMIT_SETTLE_MS);
4822
- }
4823
- function resolveSubmitNudgeTiming() {
4824
- return {
4825
- intervalMs: envMs("CONVEYOR_PTY_NUDGE_INTERVAL_MS", SUBMIT_NUDGE_INTERVAL_MS),
4826
- slowIntervalMs: envMs("CONVEYOR_PTY_NUDGE_SLOW_INTERVAL_MS", SUBMIT_NUDGE_SLOW_INTERVAL_MS),
4827
- maxPresses: SUBMIT_NUDGE_MAX_PRESSES,
4828
- windowMs: envMs("CONVEYOR_PTY_NUDGE_WINDOW_MS", SUBMIT_NUDGE_WINDOW_MS)
4829
- };
4830
- }
4831
- function resolvePlanDialogTiming() {
4832
- return {
4833
- firstPressMs: envMs("CONVEYOR_PTY_PLAN_DIALOG_FIRST_PRESS_MS", PLAN_DIALOG_FIRST_PRESS_MS),
4834
- intervalMs: envMs("CONVEYOR_PTY_PLAN_DIALOG_INTERVAL_MS", PLAN_DIALOG_INTERVAL_MS),
4835
- slowIntervalMs: envMs(
4836
- "CONVEYOR_PTY_PLAN_DIALOG_SLOW_INTERVAL_MS",
4837
- PLAN_DIALOG_SLOW_INTERVAL_MS
4838
- ),
4839
- fastWindowMs: envMs("CONVEYOR_PTY_PLAN_DIALOG_FAST_WINDOW_MS", PLAN_DIALOG_FAST_WINDOW_MS),
4840
- windowMs: envMs("CONVEYOR_PTY_PLAN_DIALOG_WINDOW_MS", PLAN_DIALOG_WINDOW_MS)
4841
- };
4842
- }
4843
- function turnOptionsFrom(options) {
4844
- return {
4845
- canUseTool: options.canUseTool,
4846
- promptDelivery: options.promptDelivery,
4847
- planDialogAutoAccept: options.planDialogAutoAccept,
4848
- abortController: options.abortController
4849
- };
4850
- }
4851
- function isRecord3(value) {
4852
- return typeof value === "object" && value !== null;
4853
- }
4854
- function extractSpawn(mod) {
4855
- if (!isRecord3(mod)) return null;
4856
- if (typeof mod.spawn === "function") return mod.spawn;
4857
- const def = mod.default;
4858
- if (isRecord3(def) && typeof def.spawn === "function") return def.spawn;
4859
- return null;
4860
- }
4861
- async function loadPtySpawn() {
4862
- const mod = await import("node-pty");
4863
- const spawn2 = extractSpawn(mod);
4864
- if (!spawn2) throw new Error("node-pty: spawn export not found");
4865
- return spawn2;
4866
- }
4867
- function inheritedEnv(socketPath) {
4868
- const env = {};
4869
- for (const [key, value] of Object.entries(process.env)) {
4870
- if (typeof value === "string") env[key] = value;
4871
- }
4872
- if (env.CLAUDE_CODE_OAUTH_TOKEN) {
4873
- delete env.ANTHROPIC_API_KEY;
4874
- }
4875
- if (socketPath) {
4876
- env.CONVEYOR_HOOK_SOCKET = socketPath;
4877
- }
4878
- env.MCP_TIMEOUT ??= "60000";
4879
- env.MCP_TOOL_TIMEOUT ??= "180000";
4880
- return env;
4881
- }
4882
- function buildPromptBytes(text) {
4883
- return `\x1B[200~${text}\x1B[201~`;
4884
- }
4885
- function renderPromptContentText(content) {
4886
- return content.map((block) => {
4887
- const b = block;
4888
- if (b?.type === "text" && typeof b.text === "string") return b.text;
4889
- if (b?.type === "image") {
4890
- return `[Image attachment \u2014 use list_task_files / get_attachment to view]`;
4891
- }
4892
- return JSON.stringify(block);
4893
- }).join("\n\n");
4894
- }
4895
- async function transcriptSize(path4) {
4896
- try {
4897
- return (await stat2(path4)).size;
4898
- } catch {
4899
- return 0;
4900
- }
4901
- }
4902
- function parseUserQuestions(input) {
4903
- if (!Array.isArray(input.questions)) return [];
4904
- const questions = [];
4905
- for (const entry of input.questions) {
4906
- if (!isRecord3(entry)) continue;
4907
- if (typeof entry.question !== "string") continue;
4908
- const options = Array.isArray(entry.options) ? entry.options.filter(isRecord3).filter((o) => typeof o.label === "string").map((o) => ({
4909
- label: o.label,
4910
- description: typeof o.description === "string" ? o.description : ""
4911
- })) : [];
4912
- questions.push({
4913
- question: entry.question,
4914
- header: typeof entry.header === "string" ? entry.header : "",
4915
- options,
4916
- ...typeof entry.multiSelect === "boolean" ? { multiSelect: entry.multiSelect } : {}
4917
- });
4918
- }
4919
- return questions;
4920
- }
4921
-
4922
4956
  // src/harness/pty/adapters/claude.ts
4923
4957
  var ClaudeTuiAdapter = class {
4924
4958
  id = "claude-code";
@@ -5029,6 +5063,18 @@ var PtySession = class {
5029
5063
  // Submit-nudge state (see armSubmitNudge).
5030
5064
  pendingSubmitNudge = false;
5031
5065
  submitNudgeTimer = null;
5066
+ // Synthetic AskUserQuestion chat-card state. The CLI does NOT flush the
5067
+ // assistant record holding a pending AskUserQuestion tool_use to the
5068
+ // transcript until the questionnaire resolves (verified live on CLI 2.1.209:
5069
+ // dialog parked on screen, transcript untouched) — so a transcript-derived
5070
+ // question card could only ever render AFTER the human answered in the raw
5071
+ // terminal. Instead the PreToolUse hook (which fires at ask time and carries
5072
+ // the full questions input) emits a synthetic `tool_use` chat event under an
5073
+ // `aq-…` id. When the real records eventually flush, the duplicate tool_use
5074
+ // is dropped (FIFO match below) and its tool_result is re-pointed at the
5075
+ // synthetic id so the card flips to answered.
5076
+ pendingSyntheticQuestionIds = [];
5077
+ questionResultRemap = /* @__PURE__ */ new Map();
5032
5078
  // Per-turn state: the prompt to feed and the per-turn options subset. Both
5033
5079
  // start from the constructor args (turn 1) and are replaced by beginTurn.
5034
5080
  turnPrompt;
@@ -5150,6 +5196,7 @@ var PtySession = class {
5150
5196
  this.passiveSignaled = false;
5151
5197
  this.disarmSubmitNudge();
5152
5198
  this.disarmPlanDialogAutoAccept();
5199
+ this.closeSyntheticQuestionCards();
5153
5200
  }
5154
5201
  /**
5155
5202
  * (Re)register the abort→teardown listener on the current turn's controller,
@@ -5181,6 +5228,7 @@ var PtySession = class {
5181
5228
  */
5182
5229
  endTurn(clean) {
5183
5230
  this.lastTurnCleanResult = clean;
5231
+ this.closeSyntheticQuestionCards();
5184
5232
  this.activeQueue?.close();
5185
5233
  this.activeQueue = null;
5186
5234
  if (this.abortHandler && this.turn.abortController) {
@@ -5255,17 +5303,72 @@ var PtySession = class {
5255
5303
  const transcriptPath = sessionTranscriptPath(this.options.cwd, sessionId);
5256
5304
  await mkdir3(dirname(transcriptPath), { recursive: true });
5257
5305
  const startOffset = this.resume ? await transcriptSize(transcriptPath) : 0;
5258
- const sendChat = this.bridge?.sendChatEvent?.bind(this.bridge);
5259
5306
  this.tailer = new JsonlTailer(
5260
5307
  transcriptPath,
5261
5308
  (event) => this.handleTranscriptEvent(event),
5262
- sendChat ? (raw) => {
5263
- for (const chatEvent of mapChatRecords(raw)) sendChat(chatEvent);
5264
- } : void 0
5309
+ typeof this.bridge?.sendChatEvent === "function" ? (raw) => this.relayChatRecord(raw) : void 0
5265
5310
  );
5266
5311
  this.tailer.start(startOffset);
5267
5312
  return { settingsPath, socketPath };
5268
5313
  }
5314
+ /**
5315
+ * Project a tailed transcript record to chat events, reconciling them with
5316
+ * any synthetic question card already emitted at hook time: the flushed
5317
+ * AskUserQuestion `tool_use` duplicate is dropped (its real id remembered),
5318
+ * and the paired `tool_result` is re-pointed at the synthetic id so the
5319
+ * live-rendered card is the one that flips to answered.
5320
+ */
5321
+ relayChatRecord(raw) {
5322
+ for (const event of mapChatRecords(raw)) {
5323
+ if (event.kind === "tool_use" && event.name === "AskUserQuestion") {
5324
+ const syntheticId = this.pendingSyntheticQuestionIds.shift();
5325
+ if (syntheticId) {
5326
+ if (event.id) this.questionResultRemap.set(event.id, syntheticId);
5327
+ continue;
5328
+ }
5329
+ } else if (event.kind === "tool_result" && event.toolUseId) {
5330
+ const syntheticId = this.questionResultRemap.get(event.toolUseId);
5331
+ if (syntheticId) {
5332
+ this.questionResultRemap.delete(event.toolUseId);
5333
+ this.sendChatEvent({ ...event, toolUseId: syntheticId });
5334
+ continue;
5335
+ }
5336
+ }
5337
+ this.sendChatEvent(event);
5338
+ }
5339
+ }
5340
+ sendChatEvent(event) {
5341
+ this.bridge?.sendChatEvent?.(event);
5342
+ }
5343
+ /** Render the question card in the web chat NOW — at hook time — instead of
5344
+ * whenever the CLI flushes the transcript records (which is only after the
5345
+ * questionnaire resolves; see the field comment). */
5346
+ emitSyntheticQuestionCard(questions) {
5347
+ if (questions.length === 0 || typeof this.bridge?.sendChatEvent !== "function") return;
5348
+ const id = `aq-${randomUUID2()}`;
5349
+ this.pendingSyntheticQuestionIds.push(id);
5350
+ this.sendChatEvent({
5351
+ kind: "tool_use",
5352
+ name: "AskUserQuestion",
5353
+ input: compactQuestionsJson(questions),
5354
+ id
5355
+ });
5356
+ }
5357
+ /**
5358
+ * Close any still-open synthetic question cards. The questionnaire can only
5359
+ * outlive its card via a path that never flushes the paired records — Esc /
5360
+ * interrupt, a superseding turn, or process teardown — so an answering
5361
+ * tool_result will never arrive for these ids; emit one so the web card
5362
+ * stops soliciting input for a dialog that no longer exists.
5363
+ */
5364
+ closeSyntheticQuestionCards() {
5365
+ const orphaned = [...this.pendingSyntheticQuestionIds, ...this.questionResultRemap.values()];
5366
+ this.pendingSyntheticQuestionIds = [];
5367
+ this.questionResultRemap.clear();
5368
+ for (const toolUseId of orphaned) {
5369
+ this.sendChatEvent({ kind: "tool_result", toolUseId, output: "", isError: false });
5370
+ }
5371
+ }
5269
5372
  writeStdin(text) {
5270
5373
  this.pty?.write(text);
5271
5374
  }
@@ -5299,6 +5402,7 @@ var PtySession = class {
5299
5402
  this._toreDown = true;
5300
5403
  this.disarmPlanDialogAutoAccept();
5301
5404
  this.disarmSubmitNudge();
5405
+ this.closeSyntheticQuestionCards();
5302
5406
  this.unsubInput?.();
5303
5407
  this.unsubInput = null;
5304
5408
  this.unsubResize?.();
@@ -5469,10 +5573,9 @@ var PtySession = class {
5469
5573
  if (request.tool_name === "AskUserQuestion") {
5470
5574
  this.disarmSubmitNudge();
5471
5575
  this.disarmPlanDialogAutoAccept();
5472
- this.pushEvent({
5473
- type: "user_question",
5474
- questions: parseUserQuestions(request.tool_input)
5475
- });
5576
+ const questions = parseUserQuestions(request.tool_input);
5577
+ this.pushEvent({ type: "user_question", questions });
5578
+ this.emitSyntheticQuestionCard(questions);
5476
5579
  return { decision: "allow" };
5477
5580
  }
5478
5581
  const canUseTool = this.turn.canUseTool;
@@ -7582,7 +7685,16 @@ function buildForceUpdateTaskStatusTool(connection) {
7582
7685
  "force_update_task_status",
7583
7686
  "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.",
7584
7687
  {
7585
- status: z9.enum(["InProgress", "ReviewPR", "ReviewDev", "Complete"]).describe("The new status for the task"),
7688
+ status: z9.enum([
7689
+ "Planning",
7690
+ "Open",
7691
+ "InProgress",
7692
+ "ReviewPR",
7693
+ "ReviewDev",
7694
+ "ReviewLive",
7695
+ "Complete",
7696
+ "Cancelled"
7697
+ ]).describe("The new status for the task"),
7586
7698
  task_id: z9.string().optional().describe("Child task ID to update. Omit to update the current task.")
7587
7699
  },
7588
7700
  async ({ status, task_id }) => {
@@ -8399,42 +8511,52 @@ function buildPmTools(connection, options) {
8399
8511
  // src/tools/discovery-tools.ts
8400
8512
  import { z as z13 } from "zod";
8401
8513
  var SP_DESCRIPTION2 = "Story point value (1=Common, 2=Magic, 3=Rare, 5=Unique). The key is 'storyPointValue' \u2014 not 'storyPoints'.";
8402
- var VALID_PROPERTY_KEYS = "title, storyPointValue, tagNames, githubPRUrl, githubBranch";
8514
+ var VALID_PROPERTY_KEYS = "title, storyPointValue, tagNames, githubPRUrl, githubBranch, risk";
8515
+ function describeUpdatedFields(p) {
8516
+ const fields = [];
8517
+ if (p.title !== void 0) fields.push(`title to "${p.title}"`);
8518
+ if (p.storyPointValue !== void 0) fields.push(`story points to ${p.storyPointValue}`);
8519
+ if (p.tagNames !== void 0) fields.push(`tags (${p.tagNames.length} tag(s))`);
8520
+ if (p.githubPRUrl !== void 0) fields.push(`PR link to "${p.githubPRUrl}"`);
8521
+ if (p.githubBranch !== void 0) fields.push(`branch to "${p.githubBranch}"`);
8522
+ if (p.risk !== void 0) fields.push(`risk to ${p.risk ?? "cleared"}`);
8523
+ return fields;
8524
+ }
8403
8525
  function buildDiscoveryTools(connection) {
8404
8526
  return [
8405
8527
  defineTool(
8406
8528
  "update_task_properties",
8407
- "Set one or more task properties in a single call. Valid keys: title, storyPointValue, tagNames, githubPRUrl, githubBranch. All are optional \u2014 include only the ones you want to update (at least one). Unknown keys are rejected.",
8529
+ "Set one or more task properties in a single call. Valid keys: title, storyPointValue, tagNames, githubPRUrl, githubBranch, risk. All are optional \u2014 include only the ones you want to update (at least one). Unknown keys are rejected.",
8408
8530
  {
8409
8531
  title: z13.string().optional().describe("The new task title"),
8410
8532
  storyPointValue: z13.number().optional().describe(SP_DESCRIPTION2),
8411
8533
  tagNames: z13.array(z13.string()).optional().describe("Array of tag names to assign"),
8412
8534
  githubPRUrl: z13.string().url().optional().describe("GitHub pull request URL to link to this task"),
8413
- githubBranch: z13.string().optional().describe("Set the GitHub branch name for this task (e.g. 'conveyor/my-feature-abc123')")
8535
+ githubBranch: z13.string().optional().describe("Set the GitHub branch name for this task (e.g. 'conveyor/my-feature-abc123')"),
8536
+ risk: z13.enum(["critical", "high", "medium", "low"]).nullable().optional().describe(
8537
+ "Risk level \u2014 how much important surface the task touches (critical/high/medium/low). Pass null to clear."
8538
+ )
8414
8539
  },
8415
- async ({ title, storyPointValue, tagNames, githubPRUrl, githubBranch }) => {
8540
+ async ({ title, storyPointValue, tagNames, githubPRUrl, githubBranch, risk }) => {
8416
8541
  try {
8417
- const nothingToUpdate = title === void 0 && storyPointValue === void 0 && tagNames === void 0 && githubPRUrl === void 0 && githubBranch === void 0;
8418
- if (nothingToUpdate) {
8542
+ const params = {
8543
+ title,
8544
+ storyPointValue,
8545
+ tagNames,
8546
+ githubPRUrl,
8547
+ githubBranch,
8548
+ risk
8549
+ };
8550
+ const updatedFields = describeUpdatedFields(params);
8551
+ if (updatedFields.length === 0) {
8419
8552
  return textResult(
8420
8553
  `No task properties were updated: none of the recognized keys were provided. Valid keys: ${VALID_PROPERTY_KEYS}. (Story points are set via 'storyPointValue', not 'storyPoints'.)`
8421
8554
  );
8422
8555
  }
8423
8556
  await connection.call("updateTaskProperties", {
8424
8557
  sessionId: connection.sessionId,
8425
- title,
8426
- storyPointValue,
8427
- tagNames,
8428
- githubPRUrl,
8429
- githubBranch
8558
+ ...params
8430
8559
  });
8431
- const updatedFields = [];
8432
- if (title !== void 0) updatedFields.push(`title to "${title}"`);
8433
- if (storyPointValue !== void 0)
8434
- updatedFields.push(`story points to ${storyPointValue}`);
8435
- if (tagNames !== void 0) updatedFields.push(`tags (${tagNames.length} tag(s))`);
8436
- if (githubPRUrl !== void 0) updatedFields.push(`PR link to "${githubPRUrl}"`);
8437
- if (githubBranch !== void 0) updatedFields.push(`branch to "${githubBranch}"`);
8438
8560
  return textResult(`Task properties updated: ${updatedFields.join(", ")}`);
8439
8561
  } catch (error) {
8440
8562
  return textResult(
@@ -12107,12 +12229,12 @@ export {
12107
12229
  DEFAULT_LIFECYCLE_CONFIG,
12108
12230
  Lifecycle,
12109
12231
  defineTool,
12110
- cleanTerminalOutput,
12111
- buildSynthesizedCredentials,
12112
- claudeJsonPath,
12113
12232
  loadPtySpawn,
12114
12233
  inheritedEnv,
12115
12234
  buildPromptBytes,
12235
+ cleanTerminalOutput,
12236
+ buildSynthesizedCredentials,
12237
+ claudeJsonPath,
12116
12238
  ClaudeTuiAdapter,
12117
12239
  createServiceLogger,
12118
12240
  PtyHarness,
@@ -12150,4 +12272,4 @@ export {
12150
12272
  runStartCommand,
12151
12273
  unshallowRepo
12152
12274
  };
12153
- //# sourceMappingURL=chunk-AJZIO5QI.js.map
12275
+ //# sourceMappingURL=chunk-PXQJ4NVO.js.map