opencara 0.108.1 → 0.109.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.
package/dist/bin.js CHANGED
@@ -1153,6 +1153,7 @@ function runAcpJob(opts) {
1153
1153
  const mcpServers = [host.acpServerEntry()];
1154
1154
  const shimSupportsLoad = initResult.agentCapabilities?.loadSession === true;
1155
1155
  let sessionId;
1156
+ let resumed = false;
1156
1157
  if (acpSpec.priorSessionId && shimSupportsLoad) {
1157
1158
  await client.loadSession({
1158
1159
  sessionId: acpSpec.priorSessionId,
@@ -1160,6 +1161,7 @@ function runAcpJob(opts) {
1160
1161
  mcpServers
1161
1162
  });
1162
1163
  sessionId = acpSpec.priorSessionId;
1164
+ resumed = true;
1163
1165
  } else {
1164
1166
  const session = await client.newSession({ cwd, mcpServers });
1165
1167
  sessionId = session.sessionId;
@@ -1173,7 +1175,10 @@ function runAcpJob(opts) {
1173
1175
  result = { exitCode: 1, stopReason: "cancelled", sessionId };
1174
1176
  return result;
1175
1177
  }
1176
- const prompt = buildPromptContent(acpSpec);
1178
+ const prompt = buildPromptContent({
1179
+ ...acpSpec,
1180
+ history: resumed ? [] : acpSpec.history
1181
+ });
1177
1182
  const promptResult = await client.prompt({
1178
1183
  sessionId,
1179
1184
  prompt,
@@ -1307,7 +1312,7 @@ function resolveLocalAcpAdapter(command, args) {
1307
1312
  }
1308
1313
 
1309
1314
  // src/commands/run.ts
1310
- var PKG_VERSION = "0.108.1";
1315
+ var PKG_VERSION = "0.109.0";
1311
1316
  var LOG_FLUSH_MS = 800;
1312
1317
  var MAX_CHUNK_SIZE = 4 * 1024;
1313
1318
  async function run(opts = {}) {
@@ -1683,20 +1688,34 @@ function worktreeCreate(args) {
1683
1688
  mkdirSync2(join2(cacheDir, ".git", "lfs", "objects"), { recursive: true });
1684
1689
  }
1685
1690
  }
1691
+ let reused = false;
1686
1692
  if (existsSync4(join2(checkoutDir, ".git"))) {
1687
- git(checkoutDir, ["fetch", "origin"], gitEnv);
1688
- if (refExists(checkoutDir, `refs/remotes/origin/${branch}`)) {
1689
- git(checkoutDir, ["checkout", "-B", branch, `origin/${branch}`], gitEnv);
1690
- } else if (refExists(checkoutDir, `refs/heads/${branch}`)) {
1691
- git(checkoutDir, ["checkout", branch], gitEnv);
1692
- } else if (fromBranch) {
1693
- git(checkoutDir, ["checkout", "-B", branch, `origin/${fromBranch}`], gitEnv);
1694
- } else {
1695
- fail(
1696
- `worktree create: '${branch}' missing locally and on origin/, no --from-branch to fall back to`
1693
+ try {
1694
+ git(checkoutDir, ["reset", "--hard", "HEAD"], gitEnv);
1695
+ git(checkoutDir, ["clean", "-fdx"], gitEnv);
1696
+ git(checkoutDir, ["fetch", "origin"], gitEnv);
1697
+ if (refExists(checkoutDir, `refs/remotes/origin/${branch}`)) {
1698
+ git(checkoutDir, ["checkout", "-B", branch, `origin/${branch}`], gitEnv);
1699
+ } else if (refExists(checkoutDir, `refs/heads/${branch}`)) {
1700
+ git(checkoutDir, ["checkout", branch], gitEnv);
1701
+ } else if (fromBranch) {
1702
+ git(checkoutDir, ["checkout", "-B", branch, `origin/${fromBranch}`], gitEnv);
1703
+ } else {
1704
+ fail(
1705
+ `worktree create: '${branch}' missing locally and on origin/, no --from-branch to fall back to`
1706
+ );
1707
+ }
1708
+ reused = true;
1709
+ } catch (err) {
1710
+ console.warn(
1711
+ `[worktree] reuse of ${checkoutDir} failed (${err.message}); re-cloning`
1697
1712
  );
1713
+ rmSync(checkoutDir, { recursive: true, force: true });
1698
1714
  }
1699
- } else {
1715
+ } else if (existsSync4(checkoutDir)) {
1716
+ rmSync(checkoutDir, { recursive: true, force: true });
1717
+ }
1718
+ if (!reused) {
1700
1719
  mkdirSync2(checkoutDir, { recursive: true });
1701
1720
  const cloneArgs = ["-c", `credential.helper=${HELPER_SNIPPET}`, "clone"];
1702
1721
  if (cacheDir) {
@@ -182,27 +182,43 @@ async function runClaudeTurn(sessionId, state, promptText, permissionMode) {
182
182
  });
183
183
  });
184
184
  }
185
- function handleClaudeEvent(sessionId, raw, done) {
186
- if (raw === null || typeof raw !== "object" || Array.isArray(raw)) return;
185
+ function translateClaudeEvent(sessionId, raw) {
186
+ const out = [];
187
+ if (raw === null || typeof raw !== "object" || Array.isArray(raw)) {
188
+ return { notifications: out };
189
+ }
187
190
  const msg = raw;
188
191
  const type = typeof msg["type"] === "string" ? msg["type"] : "";
189
192
  if (type === "assistant") {
190
- return;
193
+ const message = msg["message"];
194
+ if (message && typeof message === "object" && !Array.isArray(message)) {
195
+ const content = message.content;
196
+ if (Array.isArray(content)) {
197
+ for (const block of content) {
198
+ const translated = translateAssistantBlock(sessionId, block);
199
+ if (translated) out.push(...translated);
200
+ }
201
+ }
202
+ }
203
+ return { notifications: out };
191
204
  }
192
205
  if (type === "stream_event") {
193
206
  const event = msg["event"];
194
- if (event?.type !== "content_block_delta") return;
195
- if (event.delta?.type !== "text_delta") return;
207
+ if (event?.type !== "content_block_delta") return { notifications: out };
208
+ if (event.delta?.type !== "text_delta") return { notifications: out };
196
209
  const text = typeof event.delta.text === "string" ? event.delta.text : "";
197
- if (text.length === 0) return;
198
- notify("session/update", {
199
- sessionId,
200
- update: {
201
- sessionUpdate: "agent_message_chunk",
202
- content: { type: "text", text }
210
+ if (text.length === 0) return { notifications: out };
211
+ out.push({
212
+ method: "session/update",
213
+ params: {
214
+ sessionId,
215
+ update: {
216
+ sessionUpdate: "agent_message_chunk",
217
+ content: { type: "text", text }
218
+ }
203
219
  }
204
220
  });
205
- return;
221
+ return { notifications: out };
206
222
  }
207
223
  if (type === "result") {
208
224
  const subtype = typeof msg["subtype"] === "string" ? msg["subtype"] : "";
@@ -210,30 +226,102 @@ function handleClaudeEvent(sessionId, raw, done) {
210
226
  if (isError) {
211
227
  const resultText = typeof msg["result"] === "string" ? msg["result"] : "";
212
228
  if (resultText.length > 0) {
213
- notify("session/update", {
214
- sessionId,
215
- update: {
216
- sessionUpdate: "agent_message_chunk",
217
- content: { type: "text", text: `
229
+ out.push({
230
+ method: "session/update",
231
+ params: {
232
+ sessionId,
233
+ update: {
234
+ sessionUpdate: "agent_message_chunk",
235
+ content: { type: "text", text: `
218
236
 
219
237
  [claude error: ${resultText}]` }
238
+ }
220
239
  }
221
240
  });
222
241
  }
223
- done("refusal");
224
- return;
242
+ return { notifications: out, stopReason: "refusal" };
225
243
  }
226
244
  if (subtype === "error_max_turns") {
227
- done("max_turn_requests");
228
- return;
245
+ return { notifications: out, stopReason: "max_turn_requests" };
229
246
  }
230
247
  if (subtype === "error_max_tokens") {
231
- done("max_tokens");
232
- return;
248
+ return { notifications: out, stopReason: "max_tokens" };
233
249
  }
234
- done("end_turn");
235
- return;
250
+ return { notifications: out, stopReason: "end_turn" };
251
+ }
252
+ return { notifications: out };
253
+ }
254
+ function translateAssistantBlock(sessionId, block) {
255
+ if (!block || typeof block !== "object" || Array.isArray(block)) return null;
256
+ const b = block;
257
+ if (b["type"] !== "tool_use") return null;
258
+ if (b["name"] !== "AskUserQuestion") return null;
259
+ const input = b["input"];
260
+ if (!input || typeof input !== "object" || Array.isArray(input)) return null;
261
+ const questions = input.questions;
262
+ if (!Array.isArray(questions)) return null;
263
+ const chunks = [];
264
+ for (const q of questions) {
265
+ const rendered = renderAskUserQuestionItem(q);
266
+ if (rendered) chunks.push(rendered);
236
267
  }
268
+ if (chunks.length === 0) return null;
269
+ const text = `
270
+
271
+ ${chunks.join("\n\n")}
272
+ `;
273
+ return [
274
+ {
275
+ method: "session/update",
276
+ params: {
277
+ sessionId,
278
+ update: {
279
+ sessionUpdate: "agent_message_chunk",
280
+ content: { type: "text", text }
281
+ }
282
+ }
283
+ }
284
+ ];
285
+ }
286
+ function renderAskUserQuestionItem(raw) {
287
+ if (!raw || typeof raw !== "object" || Array.isArray(raw)) return null;
288
+ const q = raw;
289
+ const question = typeof q["question"] === "string" ? q["question"].trim() : "";
290
+ if (!question) return null;
291
+ const header = typeof q["header"] === "string" ? q["header"].trim() : "";
292
+ const multiSelect = q["multiSelect"] === true;
293
+ const rawOptions = Array.isArray(q["options"]) ? q["options"] : [];
294
+ const options = [];
295
+ for (const opt of rawOptions) {
296
+ if (!opt || typeof opt !== "object" || Array.isArray(opt)) continue;
297
+ const o = opt;
298
+ const label = typeof o["label"] === "string" ? o["label"].trim() : "";
299
+ if (!label) continue;
300
+ const value = header ? `${header}: ${label}` : label;
301
+ options.push({ label, value });
302
+ }
303
+ if (options.length === 0) return null;
304
+ const promptParts = [`**${question}**`];
305
+ if (multiSelect) {
306
+ promptParts.push(
307
+ "_(Multiple answers expected \u2014 click one, then type any others.)_"
308
+ );
309
+ } else {
310
+ promptParts.push(
311
+ "_(Pick an option, or type your own answer below.)_"
312
+ );
313
+ }
314
+ const payload = {
315
+ type: "options",
316
+ text: promptParts.join(" "),
317
+ options
318
+ };
319
+ return "```json\n" + JSON.stringify(payload, null, 2) + "\n```";
320
+ }
321
+ function handleClaudeEvent(sessionId, raw, done) {
322
+ const { notifications, stopReason } = translateClaudeEvent(sessionId, raw);
323
+ for (const n of notifications) notify(n.method, n.params);
324
+ if (stopReason) done(stopReason);
237
325
  }
238
326
  function handleInitialize(_params) {
239
327
  return {
@@ -376,5 +464,6 @@ export {
376
464
  handleInitialize,
377
465
  handleLoadSession,
378
466
  handleNewSession,
379
- sessions
467
+ sessions,
468
+ translateClaudeEvent
380
469
  };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "opencara",
3
- "version": "0.108.1",
3
+ "version": "0.109.0",
4
4
  "description": "OpenCara agent-host CLI: register a machine as an agent host and run dispatched agents.",
5
5
  "license": "MIT",
6
6
  "repository": {