@rallycry/conveyor-agent 9.0.3 → 9.1.0

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.
@@ -541,24 +541,23 @@ var AgentConnection = class _AgentConnection {
541
541
  };
542
542
  }
543
543
  // ── Convenience methods (thin wrappers around call / emit) ─────────
544
- async emitStatus(status) {
544
+ async emitStatus(status, reason) {
545
545
  this.lastEmittedStatus = status;
546
546
  await this.flushEvents();
547
+ const payload = {
548
+ sessionId: this.config.sessionId,
549
+ status,
550
+ ...reason ? { reason } : {}
551
+ };
547
552
  const AWAIT_STATUSES = ["idle", "waiting_for_input", "connected"];
548
553
  if (AWAIT_STATUSES.includes(status)) {
549
554
  try {
550
- await this.call("reportAgentStatus", {
551
- sessionId: this.config.sessionId,
552
- status
553
- });
555
+ await this.call("reportAgentStatus", payload);
554
556
  this.lastReportedStatus = status;
555
557
  } catch {
556
558
  }
557
559
  } else {
558
- void this.call("reportAgentStatus", {
559
- sessionId: this.config.sessionId,
560
- status
561
- }).then(() => {
560
+ void this.call("reportAgentStatus", payload).then(() => {
562
561
  this.lastReportedStatus = status;
563
562
  }).catch(() => {
564
563
  });
@@ -1287,6 +1286,7 @@ const net = require("node:net");
1287
1286
  const crypto = require("node:crypto");
1288
1287
 
1289
1288
  const PRE_TOOL_USE_TIMEOUT_MS = 110000;
1289
+ const ASK_USER_QUESTION_TIMEOUT_MS = 5000;
1290
1290
 
1291
1291
  let raw = "";
1292
1292
  process.stdin.setEncoding("utf8");
@@ -1358,17 +1358,26 @@ function preToolUse(payload) {
1358
1358
  );
1359
1359
  process.exit(0);
1360
1360
  };
1361
- const denyUnavailable = () =>
1362
- respond(
1363
- "deny",
1364
- "Conveyor validation is unavailable right now. Wait a moment and call the tool again.",
1365
- );
1361
+ // AskUserQuestion is observe-only (status reporting) \u2014 fail OPEN so a
1362
+ // missing/slow socket never denies the questionnaire. Everything else
1363
+ // (ExitPlanMode) is a Conveyor gate \u2014 fail CLOSED.
1364
+ const failOpen = payload.tool_name === "AskUserQuestion";
1365
+ const respondUnavailable = () =>
1366
+ failOpen
1367
+ ? respond("allow")
1368
+ : respond(
1369
+ "deny",
1370
+ "Conveyor validation is unavailable right now. Wait a moment and call the tool again.",
1371
+ );
1366
1372
  if (!socketPath) {
1367
- denyUnavailable();
1373
+ respondUnavailable();
1368
1374
  return;
1369
1375
  }
1370
1376
  const id = crypto.randomBytes(8).toString("hex");
1371
- const timer = setTimeout(denyUnavailable, PRE_TOOL_USE_TIMEOUT_MS);
1377
+ const timer = setTimeout(
1378
+ respondUnavailable,
1379
+ failOpen ? ASK_USER_QUESTION_TIMEOUT_MS : PRE_TOOL_USE_TIMEOUT_MS,
1380
+ );
1372
1381
  const client = net.connect(socketPath, () => {
1373
1382
  client.write(
1374
1383
  JSON.stringify({
@@ -1403,11 +1412,11 @@ function preToolUse(payload) {
1403
1412
  });
1404
1413
  client.on("error", () => {
1405
1414
  clearTimeout(timer);
1406
- denyUnavailable();
1415
+ respondUnavailable();
1407
1416
  });
1408
1417
  client.on("close", () => {
1409
1418
  clearTimeout(timer);
1410
- denyUnavailable();
1419
+ respondUnavailable();
1411
1420
  });
1412
1421
  }
1413
1422
  `;
@@ -1445,6 +1454,13 @@ async function writeHookSettings(dir) {
1445
1454
  {
1446
1455
  matcher: "ExitPlanMode",
1447
1456
  hooks: [{ type: "command", command: `node ${JSON.stringify(helperPath)}`, timeout: 120 }]
1457
+ },
1458
+ {
1459
+ // Observe-only: lets the session report waiting_for_input the moment
1460
+ // the planning questionnaire renders. The helper fails open for this
1461
+ // tool, so the questionnaire is never blocked by Conveyor.
1462
+ matcher: "AskUserQuestion",
1463
+ hooks: [{ type: "command", command: `node ${JSON.stringify(helperPath)}`, timeout: 30 }]
1448
1464
  }
1449
1465
  ],
1450
1466
  PostToolUse: [
@@ -1682,7 +1698,9 @@ var ConnectAgentRequestSchema = z.object({
1682
1698
  });
1683
1699
  var ReportAgentStatusRequestSchema = z.object({
1684
1700
  sessionId: z.string(),
1685
- status: z.string()
1701
+ status: z.string(),
1702
+ /** Why the agent reports this status (e.g. "user_question" while an AskUserQuestion questionnaire is pending in the TUI). */
1703
+ reason: z.string().optional()
1686
1704
  });
1687
1705
  var NotifyAgentVersionRequestSchema = z.object({
1688
1706
  sessionId: z.string(),
@@ -1997,6 +2015,20 @@ var GetProjectTaskSessionsRequestSchema = z2.object({
1997
2015
  projectId: z2.string(),
1998
2016
  taskId: z2.string()
1999
2017
  });
2018
+ var QueryProjectGcpLogsRequestSchema = z2.object({
2019
+ projectId: z2.string(),
2020
+ env: z2.enum(["prod", "dev", "claudespace"]).optional(),
2021
+ severity: z2.enum(["DEBUG", "INFO", "NOTICE", "WARNING", "ERROR", "CRITICAL", "ALERT", "EMERGENCY"]).optional(),
2022
+ services: z2.array(z2.string().min(1).max(200)).max(25).optional(),
2023
+ sqlInstances: z2.array(z2.string().min(1).max(200)).max(25).optional(),
2024
+ allServices: z2.boolean().optional(),
2025
+ search: z2.string().max(256).optional(),
2026
+ filter: z2.string().max(1e3).optional(),
2027
+ startTime: z2.string().optional(),
2028
+ endTime: z2.string().optional(),
2029
+ limit: z2.number().int().min(1).max(200).optional().default(50),
2030
+ pageToken: z2.string().max(4096).optional()
2031
+ });
2000
2032
  var StartProjectBuildRequestSchema = z2.object({
2001
2033
  projectId: z2.string(),
2002
2034
  taskId: z2.string(),
@@ -2175,6 +2207,7 @@ var CreateProjectSuggestionRequestSchema = z2.object({
2175
2207
  tagNames: z2.array(z2.string()).optional(),
2176
2208
  requestingUserId: z2.string().optional()
2177
2209
  });
2210
+ var AGENT_STATUS_REASON_USER_QUESTION = "user_question";
2178
2211
  var TASK_CHAT_HISTORY_LIMIT = 20;
2179
2212
  var PM_CHAT_HISTORY_LIMIT = 40;
2180
2213
  var AGENT_CHAT_HISTORY_FETCH_LIMIT = Math.max(TASK_CHAT_HISTORY_LIMIT, PM_CHAT_HISTORY_LIMIT) + 10;
@@ -3107,6 +3140,25 @@ async function transcriptSize(path) {
3107
3140
  return 0;
3108
3141
  }
3109
3142
  }
3143
+ function parseUserQuestions(input) {
3144
+ if (!Array.isArray(input.questions)) return [];
3145
+ const questions = [];
3146
+ for (const entry of input.questions) {
3147
+ if (!isRecord2(entry)) continue;
3148
+ if (typeof entry.question !== "string") continue;
3149
+ const options = Array.isArray(entry.options) ? entry.options.filter(isRecord2).filter((o) => typeof o.label === "string").map((o) => ({
3150
+ label: o.label,
3151
+ description: typeof o.description === "string" ? o.description : ""
3152
+ })) : [];
3153
+ questions.push({
3154
+ question: entry.question,
3155
+ header: typeof entry.header === "string" ? entry.header : "",
3156
+ options,
3157
+ ...typeof entry.multiSelect === "boolean" ? { multiSelect: entry.multiSelect } : {}
3158
+ });
3159
+ }
3160
+ return questions;
3161
+ }
3110
3162
  var PtySession = class {
3111
3163
  constructor(prompt, options, resume, bridge) {
3112
3164
  this.prompt = prompt;
@@ -3384,11 +3436,17 @@ var PtySession = class {
3384
3436
  }
3385
3437
  /**
3386
3438
  * PreToolUse request from the hook helper → the runner's `canUseTool`
3387
- * verdict. Only ExitPlanMode is matched by the settings hook, so this is
3388
- * the Conveyor plan-exit gate (validation + identification trigger run
3389
- * inside the callback). Absent callback → allow (mirrors the SDK default).
3439
+ * verdict. ExitPlanMode is the Conveyor plan-exit gate (validation +
3440
+ * identification trigger run inside the callback); AskUserQuestion is
3441
+ * observed and always allowed. Absent callback → allow (mirrors the SDK
3442
+ * default).
3390
3443
  */
3391
3444
  async handlePreToolUse(request) {
3445
+ if (request.tool_name === "AskUserQuestion") {
3446
+ this.disarmSubmitNudge();
3447
+ this.queue.push({ type: "user_question", questions: parseUserQuestions(request.tool_input) });
3448
+ return { decision: "allow" };
3449
+ }
3392
3450
  const canUseTool = this.options.canUseTool;
3393
3451
  let verdict;
3394
3452
  if (canUseTool) {
@@ -6456,6 +6514,44 @@ async function handleResultCase(event, host, context, startTime, isTyping, lastA
6456
6514
  }
6457
6515
 
6458
6516
  // src/execution/event-processor.ts
6517
+ function hasAskUserQuestionBlock(event) {
6518
+ return event.message.content.some((b) => b.type === "tool_use" && b.name === "AskUserQuestion");
6519
+ }
6520
+ async function armQuestionPending(host, state) {
6521
+ state.questionPending = true;
6522
+ await host.connection.emitStatus("waiting_for_input", AGENT_STATUS_REASON_USER_QUESTION);
6523
+ await host.callbacks.onStatusChange("waiting_for_input");
6524
+ }
6525
+ async function clearQuestionPending(host, state, options) {
6526
+ if (!state.questionPending) return;
6527
+ state.questionPending = false;
6528
+ if (options.emitRunning) {
6529
+ await host.connection.emitStatus("running");
6530
+ await host.callbacks.onStatusChange("running");
6531
+ }
6532
+ }
6533
+ async function applyQuestionTransitions(event, host, state) {
6534
+ switch (event.type) {
6535
+ case "user_question":
6536
+ await armQuestionPending(host, state);
6537
+ return;
6538
+ case "assistant":
6539
+ if (!hasAskUserQuestionBlock(event)) {
6540
+ await clearQuestionPending(host, state, { emitRunning: true });
6541
+ }
6542
+ return;
6543
+ case "tool_progress":
6544
+ if (event.tool_name === "AskUserQuestion") {
6545
+ await clearQuestionPending(host, state, { emitRunning: true });
6546
+ }
6547
+ return;
6548
+ case "result":
6549
+ await clearQuestionPending(host, state, { emitRunning: false });
6550
+ return;
6551
+ default:
6552
+ break;
6553
+ }
6554
+ }
6459
6555
  function stopTypingIfNeeded(host, isTyping) {
6460
6556
  if (isTyping) host.connection.sendTypingStop();
6461
6557
  }
@@ -6535,13 +6631,14 @@ async function processEvents(events, context, host) {
6535
6631
  staleSession: void 0,
6536
6632
  authError: void 0,
6537
6633
  lastAssistantUsage: void 0,
6538
- turnToolCalls: []
6634
+ turnToolCalls: [],
6635
+ questionPending: false
6539
6636
  };
6540
6637
  for await (const event of events) {
6541
6638
  if (host.isStopped()) break;
6542
6639
  flushPendingToolCalls(host, state.turnToolCalls);
6543
6640
  const now = Date.now();
6544
- if (now - lastStatusEmit >= STATUS_REEMIT_INTERVAL_MS) {
6641
+ if (now - lastStatusEmit >= STATUS_REEMIT_INTERVAL_MS && !state.questionPending) {
6545
6642
  host.connection.emitStatus("running");
6546
6643
  lastStatusEmit = now;
6547
6644
  }
@@ -6549,6 +6646,7 @@ async function processEvents(events, context, host) {
6549
6646
  stopTypingIfNeeded(host, state.isTyping);
6550
6647
  return { retriable: false, modeRestart: true };
6551
6648
  }
6649
+ await applyQuestionTransitions(event, host, state);
6552
6650
  switch (event.type) {
6553
6651
  case "system":
6554
6652
  await processSystemCase(event, host, context, state);
@@ -8893,4 +8991,4 @@ export {
8893
8991
  runStartCommand,
8894
8992
  unshallowRepo
8895
8993
  };
8896
- //# sourceMappingURL=chunk-EYIGNRRW.js.map
8994
+ //# sourceMappingURL=chunk-P3QCTUHC.js.map