@testchimp/cli 0.1.65 → 0.1.66

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.
@@ -1773,6 +1773,8 @@ export async function runChimphands(opts) {
1773
1773
  let lastUserActivity = Date.now();
1774
1774
  let exitCode;
1775
1775
  let cancelTurnRequested = false;
1776
+ /** True when cancel was triggered by a mid-turn user message (steer), not Stop. */
1777
+ let steerAbortRequested = false;
1776
1778
  let activeOpencodeChild = null;
1777
1779
  let agentTurnInProgress = false;
1778
1780
  /** Wakes waitForNextPrompt when idle is signaled (SSE) or a user message arrives. */
@@ -1788,6 +1790,35 @@ export async function runChimphands(opts) {
1788
1790
  activeOpencodeChild = child;
1789
1791
  },
1790
1792
  };
1793
+ /** Abort in-flight OpenCode turn (session HTTP abort + kill `opencode run` child). */
1794
+ const abortActiveTurn = (reason) => {
1795
+ cancelTurnRequested = true;
1796
+ if (reason === "steer") {
1797
+ steerAbortRequested = true;
1798
+ }
1799
+ const sessionToAbort = opencodeSessionId?.trim();
1800
+ if (attachUrl && sessionToAbort) {
1801
+ void abortOpencodeSession(attachUrl, sessionToAbort);
1802
+ }
1803
+ const ch = activeOpencodeChild;
1804
+ if (ch && !ch.killed) {
1805
+ try {
1806
+ ch.kill("SIGTERM");
1807
+ setTimeout(() => {
1808
+ try {
1809
+ if (!ch.killed)
1810
+ ch.kill("SIGKILL");
1811
+ }
1812
+ catch {
1813
+ /* ignore */
1814
+ }
1815
+ }, 2000);
1816
+ }
1817
+ catch {
1818
+ /* ignore */
1819
+ }
1820
+ }
1821
+ };
1791
1822
  const enqueueUserMessage = (msg) => {
1792
1823
  const id = msg.id?.trim();
1793
1824
  if (id) {
@@ -1801,9 +1832,15 @@ export async function runChimphands(opts) {
1801
1832
  queue.push(content);
1802
1833
  lastUserActivity = Date.now();
1803
1834
  idle = false;
1804
- wakePromptWaiter();
1805
1835
  const preview = content.length > 120 ? `${content.slice(0, 117)}...` : content;
1806
1836
  console.error(`ChimpHands queued user message (turnActive=${agentTurnInProgress}, depth=${queue.length}): ${JSON.stringify(preview)}`);
1837
+ // OpenCode has no mid-turn inject: abort the active turn so the queued
1838
+ // message is consumed immediately as the next prompt (course-correct).
1839
+ if (agentTurnInProgress && !cancelTurnRequested) {
1840
+ console.error(`ChimpHands steering: aborting active turn so queued user message reaches OpenCode`);
1841
+ abortActiveTurn("steer");
1842
+ }
1843
+ wakePromptWaiter();
1807
1844
  };
1808
1845
  const pollPendingUserMessages = async () => {
1809
1846
  try {
@@ -1869,29 +1906,8 @@ export async function runChimphands(opts) {
1869
1906
  childEnv.TESTCHIMP_USER_ID = userId;
1870
1907
  const handleCancelTurn = () => {
1871
1908
  console.error(`ChimpHands cancel_turn received for session ${sessionId}`);
1872
- cancelTurnRequested = true;
1873
- const sessionToAbort = opencodeSessionId?.trim();
1874
- if (attachUrl && sessionToAbort) {
1875
- void abortOpencodeSession(attachUrl, sessionToAbort);
1876
- }
1877
- const ch = activeOpencodeChild;
1878
- if (ch && !ch.killed) {
1879
- try {
1880
- ch.kill("SIGTERM");
1881
- setTimeout(() => {
1882
- try {
1883
- if (!ch.killed)
1884
- ch.kill("SIGKILL");
1885
- }
1886
- catch {
1887
- /* ignore */
1888
- }
1889
- }, 2000);
1890
- }
1891
- catch {
1892
- /* ignore */
1893
- }
1894
- }
1909
+ steerAbortRequested = false;
1910
+ abortActiveTurn("cancel");
1895
1911
  };
1896
1912
  const stopInbound = connectInboundStream(backend, apiKey, sessionId, {
1897
1913
  onUserMessage: enqueueUserMessage,
@@ -2006,11 +2022,21 @@ export async function runChimphands(opts) {
2006
2022
  });
2007
2023
  while (prompt) {
2008
2024
  cancelTurnRequested = false;
2025
+ const wasSteered = steerAbortRequested;
2026
+ steerAbortRequested = false;
2009
2027
  agentTurnInProgress = true;
2010
2028
  let useOpencodeSessionId = opencodeSessionId;
2011
2029
  let isNewOpencodeSession = !useOpencodeSessionId;
2012
- let effectivePrompt = wrapPromptWithContext(conversationSummary, prompt, isNewOpencodeSession, workingBranch, pullRequestUrl);
2013
- const turnPostEvent = (role, content, opts) => postEventForTurn(prompt, effectivePrompt, role, content, opts);
2030
+ const promptForOpencode = wasSteered
2031
+ ? [
2032
+ "The previous agent turn was interrupted because the user sent a new message.",
2033
+ "Stop any long-running watch/poll that no longer applies. Course-correct using this guidance:",
2034
+ "",
2035
+ prompt,
2036
+ ].join("\n")
2037
+ : prompt;
2038
+ let effectivePrompt = wrapPromptWithContext(conversationSummary, promptForOpencode, isNewOpencodeSession, workingBranch, pullRequestUrl);
2039
+ const turnPostEvent = (role, content, opts) => postEventForTurn(promptForOpencode, effectivePrompt, role, content, opts);
2014
2040
  // Visible in chat (not filtered as routine). OpenCode may not emit text until a
2015
2041
  // part completes — without this the UI looks empty while the turn is running.
2016
2042
  postEvent(ROLE_STATUS, "Agent is working…", { status: STATUS_RUNNING });
@@ -2098,7 +2124,11 @@ export async function runChimphands(opts) {
2098
2124
  exitCode = result.code || 1;
2099
2125
  break;
2100
2126
  }
2101
- if (result.cancelled) {
2127
+ if (result.cancelled && (steerAbortRequested || queue.length > 0)) {
2128
+ // Mid-turn user message aborted this turn — run the queued prompt next.
2129
+ postEvent(ROLE_STATUS, "Incorporating your message…", { status: STATUS_RUNNING });
2130
+ }
2131
+ else if (result.cancelled) {
2102
2132
  postEvent(ROLE_STATUS, "Turn stopped", { status: STATUS_WAITING_USER });
2103
2133
  }
2104
2134
  else {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@testchimp/cli",
3
- "version": "0.1.65",
3
+ "version": "0.1.66",
4
4
  "description": "TestChimp CLI and MCP server — coverage, plans, EaaS, TrueCoverage, API operations (calls /api/mcp/*)",
5
5
  "type": "module",
6
6
  "main": "dist/bin/testchimp.js",