opencara 0.106.0 → 0.107.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
@@ -82,12 +82,19 @@ var AcpHistoryTurnSchema = z3.object({
82
82
  role: z3.enum(["user", "assistant"]),
83
83
  text: z3.string()
84
84
  });
85
+ var AcpPermissionModeSchema = z3.enum([
86
+ "default",
87
+ "acceptEdits",
88
+ "plan",
89
+ "bypassPermissions"
90
+ ]);
85
91
  var AcpSpecSchema = z3.object({
86
92
  systemPromptMd: z3.string(),
87
93
  userPromptMd: z3.string(),
88
94
  history: z3.array(AcpHistoryTurnSchema).default([]),
89
95
  pageContextJson: z3.string().optional(),
90
- priorSessionId: z3.string().optional()
96
+ priorSessionId: z3.string().optional(),
97
+ permissionMode: AcpPermissionModeSchema.optional()
91
98
  });
92
99
  var AgentSpecSchema = z3.object({
93
100
  kind: z3.string(),
@@ -198,6 +205,11 @@ var HelloAckSchema = z4.object({
198
205
  agentHostId: z4.string(),
199
206
  deviceName: z4.string()
200
207
  });
208
+ var CancelJobSchema = z4.object({
209
+ type: z4.literal("cancel"),
210
+ runId: z4.string(),
211
+ reason: z4.enum(["user_stopped", "wave_cancelled"])
212
+ });
201
213
  var PingSchema = z4.object({ type: z4.literal("ping") });
202
214
  var PongSchema = z4.object({ type: z4.literal("pong") });
203
215
  var AgentCallEnvelope = {
@@ -225,10 +237,56 @@ var TemplateNodeConfigSetCallSchema = z4.object({
225
237
  nodeId: z4.string().min(1),
226
238
  config: z4.record(z4.string(), z4.unknown())
227
239
  });
240
+ var KanbanWaveDispatchCallSchema = z4.object({
241
+ ...AgentCallEnvelope,
242
+ kind: z4.literal("kanban.wave.dispatch"),
243
+ flowSlug: z4.string().min(1),
244
+ issueNumbers: z4.array(z4.number().int()).min(1).max(10)
245
+ });
246
+ var IssueSubissueCreateCallSchema = z4.object({
247
+ ...AgentCallEnvelope,
248
+ kind: z4.literal("issue.subissue.create"),
249
+ parentIssueNumber: z4.number().int(),
250
+ title: z4.string().min(1),
251
+ bodyMd: z4.string(),
252
+ labels: z4.array(z4.string()).optional()
253
+ });
254
+ var IssueCreateCallSchema = z4.object({
255
+ ...AgentCallEnvelope,
256
+ kind: z4.literal("issue.create"),
257
+ title: z4.string().min(1),
258
+ bodyMd: z4.string(),
259
+ labels: z4.array(z4.string()).optional()
260
+ });
261
+ var IssueStateSetCallSchema = z4.object({
262
+ ...AgentCallEnvelope,
263
+ kind: z4.literal("issue.state.set"),
264
+ issueNumber: z4.number().int(),
265
+ state: z4.enum(["open", "closed"]),
266
+ stateReason: z4.enum(["completed", "not_planned", "reopened"]).nullable().optional()
267
+ });
268
+ var IssueCommentCreateCallSchema = z4.object({
269
+ ...AgentCallEnvelope,
270
+ kind: z4.literal("issue.comment.create"),
271
+ issueNumber: z4.number().int(),
272
+ bodyMd: z4.string().min(1)
273
+ });
274
+ var IssueLabelsSetCallSchema = z4.object({
275
+ ...AgentCallEnvelope,
276
+ kind: z4.literal("issue.labels.set"),
277
+ issueNumber: z4.number().int(),
278
+ labels: z4.array(z4.string())
279
+ });
228
280
  var AgentCallSchema = z4.discriminatedUnion("kind", [
229
281
  IssueBodySetCallSchema,
230
282
  FlowNodeConfigSetCallSchema,
231
- TemplateNodeConfigSetCallSchema
283
+ TemplateNodeConfigSetCallSchema,
284
+ KanbanWaveDispatchCallSchema,
285
+ IssueSubissueCreateCallSchema,
286
+ IssueCreateCallSchema,
287
+ IssueStateSetCallSchema,
288
+ IssueCommentCreateCallSchema,
289
+ IssueLabelsSetCallSchema
232
290
  ]);
233
291
  var AgentCallRequestEnvelope = {
234
292
  type: z4.literal("agent-call-request"),
@@ -255,10 +313,56 @@ var TemplateNodeConfigSetCallRequestSchema = z4.object({
255
313
  nodeId: z4.string().min(1),
256
314
  config: z4.record(z4.string(), z4.unknown())
257
315
  });
316
+ var KanbanWaveDispatchCallRequestSchema = z4.object({
317
+ ...AgentCallRequestEnvelope,
318
+ kind: z4.literal("kanban.wave.dispatch"),
319
+ flowSlug: z4.string().min(1),
320
+ issueNumbers: z4.array(z4.number().int()).min(1).max(10)
321
+ });
322
+ var IssueSubissueCreateCallRequestSchema = z4.object({
323
+ ...AgentCallRequestEnvelope,
324
+ kind: z4.literal("issue.subissue.create"),
325
+ parentIssueNumber: z4.number().int(),
326
+ title: z4.string().min(1),
327
+ bodyMd: z4.string(),
328
+ labels: z4.array(z4.string()).optional()
329
+ });
330
+ var IssueCreateCallRequestSchema = z4.object({
331
+ ...AgentCallRequestEnvelope,
332
+ kind: z4.literal("issue.create"),
333
+ title: z4.string().min(1),
334
+ bodyMd: z4.string(),
335
+ labels: z4.array(z4.string()).optional()
336
+ });
337
+ var IssueStateSetCallRequestSchema = z4.object({
338
+ ...AgentCallRequestEnvelope,
339
+ kind: z4.literal("issue.state.set"),
340
+ issueNumber: z4.number().int(),
341
+ state: z4.enum(["open", "closed"]),
342
+ stateReason: z4.enum(["completed", "not_planned", "reopened"]).nullable().optional()
343
+ });
344
+ var IssueCommentCreateCallRequestSchema = z4.object({
345
+ ...AgentCallRequestEnvelope,
346
+ kind: z4.literal("issue.comment.create"),
347
+ issueNumber: z4.number().int(),
348
+ bodyMd: z4.string().min(1)
349
+ });
350
+ var IssueLabelsSetCallRequestSchema = z4.object({
351
+ ...AgentCallRequestEnvelope,
352
+ kind: z4.literal("issue.labels.set"),
353
+ issueNumber: z4.number().int(),
354
+ labels: z4.array(z4.string())
355
+ });
258
356
  var AgentCallRequestSchema = z4.discriminatedUnion("kind", [
259
357
  IssueBodySetCallRequestSchema,
260
358
  FlowNodeConfigSetCallRequestSchema,
261
- TemplateNodeConfigSetCallRequestSchema
359
+ TemplateNodeConfigSetCallRequestSchema,
360
+ KanbanWaveDispatchCallRequestSchema,
361
+ IssueSubissueCreateCallRequestSchema,
362
+ IssueCreateCallRequestSchema,
363
+ IssueStateSetCallRequestSchema,
364
+ IssueCommentCreateCallRequestSchema,
365
+ IssueLabelsSetCallRequestSchema
262
366
  ]);
263
367
  var AgentCallResultSchema = z4.object({
264
368
  type: z4.literal("agent-call-result"),
@@ -273,7 +377,8 @@ var ServerToDeviceMessageSchema = z4.discriminatedUnion("type", [
273
377
  JobAssignmentSchema,
274
378
  HelloAckSchema,
275
379
  PingSchema,
276
- AgentCallResultSchema
380
+ AgentCallResultSchema,
381
+ CancelJobSchema
277
382
  ]);
278
383
  var DeviceToServerMessageSchema = z4.union([
279
384
  HelloMessageSchema,
@@ -1073,9 +1178,35 @@ function runAcpJob(opts) {
1073
1178
  const translator = createUpdateTranslator(handlers.onLog);
1074
1179
  client.onSessionUpdate((p) => translator.handle(p.update));
1075
1180
  client.onStderr((chunk) => handlers.onLog("stderr", chunk));
1181
+ let activeSessionId = null;
1182
+ let cancelRequested = false;
1183
+ let forceCloseScheduled = false;
1184
+ const scheduleForceClose = () => {
1185
+ if (forceCloseScheduled) return;
1186
+ forceCloseScheduled = true;
1187
+ setTimeout(() => {
1188
+ client.close(0).catch(() => void 0);
1189
+ }, 2e3);
1190
+ };
1076
1191
  const controller = {
1077
1192
  onAgentCallResult(msg) {
1078
1193
  bridge.onResult(msg);
1194
+ },
1195
+ cancel() {
1196
+ if (cancelRequested) return;
1197
+ cancelRequested = true;
1198
+ if (activeSessionId) {
1199
+ try {
1200
+ client.cancel(activeSessionId);
1201
+ } catch (err) {
1202
+ handlers.onLog(
1203
+ "stderr",
1204
+ `[opencara] cancel notify failed: ${err instanceof Error ? err.message : String(err)}
1205
+ `
1206
+ );
1207
+ }
1208
+ }
1209
+ scheduleForceClose();
1079
1210
  }
1080
1211
  };
1081
1212
  const promise = (async () => {
@@ -1102,8 +1233,21 @@ function runAcpJob(opts) {
1102
1233
  const session = await client.newSession({ cwd, mcpServers });
1103
1234
  sessionId = session.sessionId;
1104
1235
  }
1236
+ activeSessionId = sessionId;
1237
+ if (cancelRequested) {
1238
+ try {
1239
+ client.cancel(sessionId);
1240
+ } catch {
1241
+ }
1242
+ result = { exitCode: 1, stopReason: "cancelled", sessionId };
1243
+ return result;
1244
+ }
1105
1245
  const prompt = buildPromptContent(acpSpec);
1106
- const promptResult = await client.prompt({ sessionId, prompt });
1246
+ const promptResult = await client.prompt({
1247
+ sessionId,
1248
+ prompt,
1249
+ ...acpSpec.permissionMode ? { permissionMode: acpSpec.permissionMode } : {}
1250
+ });
1107
1251
  result = {
1108
1252
  exitCode: promptResult.stopReason === "end_turn" ? 0 : 1,
1109
1253
  stopReason: promptResult.stopReason,
@@ -1232,7 +1376,7 @@ function resolveLocalAcpAdapter(command, args) {
1232
1376
  }
1233
1377
 
1234
1378
  // src/commands/run.ts
1235
- var PKG_VERSION = "0.106.0";
1379
+ var PKG_VERSION = "0.107.0";
1236
1380
  var LOG_FLUSH_MS = 800;
1237
1381
  var MAX_CHUNK_SIZE = 4 * 1024;
1238
1382
  async function run(opts = {}) {
@@ -1283,6 +1427,16 @@ function handleServerMessage(msg, client, _cfg) {
1283
1427
  acpControllers.get(msg.runId)?.onAgentCallResult(msg);
1284
1428
  return;
1285
1429
  }
1430
+ if (msg.type === "cancel") {
1431
+ const ctrl = acpControllers.get(msg.runId);
1432
+ if (ctrl) {
1433
+ console.log(
1434
+ `[opencara] job ${msg.runId.slice(-8)} cancel requested (${msg.reason})`
1435
+ );
1436
+ ctrl.cancel();
1437
+ }
1438
+ return;
1439
+ }
1286
1440
  }
1287
1441
  async function executeJob(job, client) {
1288
1442
  const runId = job.run.id;
@@ -1561,6 +1715,11 @@ function worktreeCreate(args) {
1561
1715
  if (useLfs && !useCache) {
1562
1716
  fail("--lfs requires --cache-repo");
1563
1717
  }
1718
+ if (useLfs && !hasGitLfs()) {
1719
+ fail(
1720
+ "--lfs is set but git-lfs is not installed on this host \u2014 install it (e.g. `apt install git-lfs && git lfs install`) or disable LFS on the agent.worktree.cacheRepo config"
1721
+ );
1722
+ }
1564
1723
  const cacheDir = useCache ? join2(CACHE_ROOT, safeKey(repo)) : null;
1565
1724
  const gitEnv = useCache && !useLfs ? { ...process.env, GIT_LFS_SKIP_SMUDGE: "1" } : void 0;
1566
1725
  const HELPER_SNIPPET = '!f() { echo username=x-access-token; echo "password=$GH_TOKEN"; }; f';
@@ -1733,6 +1892,16 @@ function refExists(cwd, ref) {
1733
1892
  return false;
1734
1893
  }
1735
1894
  }
1895
+ function hasGitLfs() {
1896
+ try {
1897
+ execFileSync("git", ["lfs", "version"], {
1898
+ stdio: ["ignore", "ignore", "ignore"]
1899
+ });
1900
+ return true;
1901
+ } catch {
1902
+ return false;
1903
+ }
1904
+ }
1736
1905
  function pickFlag(argv, name) {
1737
1906
  const i = argv.indexOf(name);
1738
1907
  if (i === -1) return void 0;
@@ -62,7 +62,7 @@ function notify(method, params) {
62
62
  send({ jsonrpc: "2.0", method, params });
63
63
  }
64
64
  var sessions = /* @__PURE__ */ new Map();
65
- async function runClaudeTurn(sessionId, state, promptText) {
65
+ async function runClaudeTurn(sessionId, state, promptText, permissionMode) {
66
66
  return new Promise((resolve, reject) => {
67
67
  const idFlag = state.resume ? "--resume" : "--session-id";
68
68
  const args = [
@@ -75,16 +75,19 @@ async function runClaudeTurn(sessionId, state, promptText) {
75
75
  "--include-partial-messages",
76
76
  "--verbose",
77
77
  idFlag,
78
- sessionId,
79
- // Headless: no human in the loop to approve tool use. Matches the
80
- // legacy `claudeAdapter` posture in agents/kinds.ts.
81
- "--dangerously-skip-permissions"
78
+ sessionId
82
79
  ];
80
+ if (permissionMode && permissionMode !== "default") {
81
+ args.push("--permission-mode", permissionMode);
82
+ } else {
83
+ args.push("--dangerously-skip-permissions");
84
+ }
83
85
  const child = spawn("claude", args, {
84
86
  cwd: state.cwd,
85
87
  env: process.env,
86
88
  stdio: ["pipe", "pipe", "pipe"]
87
89
  });
90
+ state.activeChild = child;
88
91
  child.stdin.on("error", () => {
89
92
  });
90
93
  child.stdin.end(promptText);
@@ -116,9 +119,14 @@ async function runClaudeTurn(sessionId, state, promptText) {
116
119
  reject(err);
117
120
  }
118
121
  });
119
- child.on("close", (code) => {
122
+ child.on("close", (code, signal) => {
123
+ state.activeChild = null;
120
124
  if (!resolved) {
121
125
  resolved = true;
126
+ if (signal === "SIGTERM" || signal === "SIGINT") {
127
+ resolve({ stopReason: "cancelled" });
128
+ return;
129
+ }
122
130
  if (code !== 0) {
123
131
  stderr.write(`[claude-acp] claude exited code=${code} without result event
124
132
  `);
@@ -225,10 +233,29 @@ async function handlePrompt(params) {
225
233
  if (promptText.length === 0) {
226
234
  throw new Error("session/prompt: no text content blocks");
227
235
  }
228
- const result = await runClaudeTurn(params.sessionId, state, promptText);
236
+ const result = await runClaudeTurn(
237
+ params.sessionId,
238
+ state,
239
+ promptText,
240
+ params.permissionMode
241
+ );
229
242
  state.resume = true;
230
243
  return { stopReason: result.stopReason };
231
244
  }
245
+ function handleCancel(params) {
246
+ const state = sessions.get(params.sessionId);
247
+ if (!state) return;
248
+ const child = state.activeChild;
249
+ if (!child) return;
250
+ try {
251
+ child.kill("SIGTERM");
252
+ } catch (err) {
253
+ stderr.write(
254
+ `[claude-acp] session/cancel kill failed: ${err instanceof Error ? err.message : String(err)}
255
+ `
256
+ );
257
+ }
258
+ }
232
259
  var isMainModule = import.meta.url === `file://${process.argv[1]}` || process.argv[1]?.endsWith("claude-acp.ts") === true || process.argv[1]?.endsWith("claude-acp.js") === true;
233
260
  if (isMainModule) {
234
261
  const decoder = new FrameDecoder();
@@ -246,7 +273,16 @@ if (isMainModule) {
246
273
  });
247
274
  }
248
275
  async function dispatch(msg) {
249
- if (!("id" in msg) || msg.id == null) return;
276
+ if (!("id" in msg) || msg.id == null) {
277
+ const notification = msg;
278
+ if (notification.method === "session/cancel") {
279
+ const params = notification.params;
280
+ if (params && typeof params.sessionId === "string") {
281
+ handleCancel(params);
282
+ }
283
+ }
284
+ return;
285
+ }
250
286
  if ("result" in msg || "error" in msg) return;
251
287
  const req = msg;
252
288
  try {
@@ -123,12 +123,19 @@ var AcpHistoryTurnSchema = z2.object({
123
123
  role: z2.enum(["user", "assistant"]),
124
124
  text: z2.string()
125
125
  });
126
+ var AcpPermissionModeSchema = z2.enum([
127
+ "default",
128
+ "acceptEdits",
129
+ "plan",
130
+ "bypassPermissions"
131
+ ]);
126
132
  var AcpSpecSchema = z2.object({
127
133
  systemPromptMd: z2.string(),
128
134
  userPromptMd: z2.string(),
129
135
  history: z2.array(AcpHistoryTurnSchema).default([]),
130
136
  pageContextJson: z2.string().optional(),
131
- priorSessionId: z2.string().optional()
137
+ priorSessionId: z2.string().optional(),
138
+ permissionMode: AcpPermissionModeSchema.optional()
132
139
  });
133
140
  var AgentSpecSchema = z2.object({
134
141
  kind: z2.string(),
@@ -239,6 +246,11 @@ var HelloAckSchema = z3.object({
239
246
  agentHostId: z3.string(),
240
247
  deviceName: z3.string()
241
248
  });
249
+ var CancelJobSchema = z3.object({
250
+ type: z3.literal("cancel"),
251
+ runId: z3.string(),
252
+ reason: z3.enum(["user_stopped", "wave_cancelled"])
253
+ });
242
254
  var PingSchema = z3.object({ type: z3.literal("ping") });
243
255
  var PongSchema = z3.object({ type: z3.literal("pong") });
244
256
  var AgentCallEnvelope = {
@@ -266,10 +278,56 @@ var TemplateNodeConfigSetCallSchema = z3.object({
266
278
  nodeId: z3.string().min(1),
267
279
  config: z3.record(z3.string(), z3.unknown())
268
280
  });
281
+ var KanbanWaveDispatchCallSchema = z3.object({
282
+ ...AgentCallEnvelope,
283
+ kind: z3.literal("kanban.wave.dispatch"),
284
+ flowSlug: z3.string().min(1),
285
+ issueNumbers: z3.array(z3.number().int()).min(1).max(10)
286
+ });
287
+ var IssueSubissueCreateCallSchema = z3.object({
288
+ ...AgentCallEnvelope,
289
+ kind: z3.literal("issue.subissue.create"),
290
+ parentIssueNumber: z3.number().int(),
291
+ title: z3.string().min(1),
292
+ bodyMd: z3.string(),
293
+ labels: z3.array(z3.string()).optional()
294
+ });
295
+ var IssueCreateCallSchema = z3.object({
296
+ ...AgentCallEnvelope,
297
+ kind: z3.literal("issue.create"),
298
+ title: z3.string().min(1),
299
+ bodyMd: z3.string(),
300
+ labels: z3.array(z3.string()).optional()
301
+ });
302
+ var IssueStateSetCallSchema = z3.object({
303
+ ...AgentCallEnvelope,
304
+ kind: z3.literal("issue.state.set"),
305
+ issueNumber: z3.number().int(),
306
+ state: z3.enum(["open", "closed"]),
307
+ stateReason: z3.enum(["completed", "not_planned", "reopened"]).nullable().optional()
308
+ });
309
+ var IssueCommentCreateCallSchema = z3.object({
310
+ ...AgentCallEnvelope,
311
+ kind: z3.literal("issue.comment.create"),
312
+ issueNumber: z3.number().int(),
313
+ bodyMd: z3.string().min(1)
314
+ });
315
+ var IssueLabelsSetCallSchema = z3.object({
316
+ ...AgentCallEnvelope,
317
+ kind: z3.literal("issue.labels.set"),
318
+ issueNumber: z3.number().int(),
319
+ labels: z3.array(z3.string())
320
+ });
269
321
  var AgentCallSchema = z3.discriminatedUnion("kind", [
270
322
  IssueBodySetCallSchema,
271
323
  FlowNodeConfigSetCallSchema,
272
- TemplateNodeConfigSetCallSchema
324
+ TemplateNodeConfigSetCallSchema,
325
+ KanbanWaveDispatchCallSchema,
326
+ IssueSubissueCreateCallSchema,
327
+ IssueCreateCallSchema,
328
+ IssueStateSetCallSchema,
329
+ IssueCommentCreateCallSchema,
330
+ IssueLabelsSetCallSchema
273
331
  ]);
274
332
  var AgentCallRequestEnvelope = {
275
333
  type: z3.literal("agent-call-request"),
@@ -296,10 +354,56 @@ var TemplateNodeConfigSetCallRequestSchema = z3.object({
296
354
  nodeId: z3.string().min(1),
297
355
  config: z3.record(z3.string(), z3.unknown())
298
356
  });
357
+ var KanbanWaveDispatchCallRequestSchema = z3.object({
358
+ ...AgentCallRequestEnvelope,
359
+ kind: z3.literal("kanban.wave.dispatch"),
360
+ flowSlug: z3.string().min(1),
361
+ issueNumbers: z3.array(z3.number().int()).min(1).max(10)
362
+ });
363
+ var IssueSubissueCreateCallRequestSchema = z3.object({
364
+ ...AgentCallRequestEnvelope,
365
+ kind: z3.literal("issue.subissue.create"),
366
+ parentIssueNumber: z3.number().int(),
367
+ title: z3.string().min(1),
368
+ bodyMd: z3.string(),
369
+ labels: z3.array(z3.string()).optional()
370
+ });
371
+ var IssueCreateCallRequestSchema = z3.object({
372
+ ...AgentCallRequestEnvelope,
373
+ kind: z3.literal("issue.create"),
374
+ title: z3.string().min(1),
375
+ bodyMd: z3.string(),
376
+ labels: z3.array(z3.string()).optional()
377
+ });
378
+ var IssueStateSetCallRequestSchema = z3.object({
379
+ ...AgentCallRequestEnvelope,
380
+ kind: z3.literal("issue.state.set"),
381
+ issueNumber: z3.number().int(),
382
+ state: z3.enum(["open", "closed"]),
383
+ stateReason: z3.enum(["completed", "not_planned", "reopened"]).nullable().optional()
384
+ });
385
+ var IssueCommentCreateCallRequestSchema = z3.object({
386
+ ...AgentCallRequestEnvelope,
387
+ kind: z3.literal("issue.comment.create"),
388
+ issueNumber: z3.number().int(),
389
+ bodyMd: z3.string().min(1)
390
+ });
391
+ var IssueLabelsSetCallRequestSchema = z3.object({
392
+ ...AgentCallRequestEnvelope,
393
+ kind: z3.literal("issue.labels.set"),
394
+ issueNumber: z3.number().int(),
395
+ labels: z3.array(z3.string())
396
+ });
299
397
  var AgentCallRequestSchema = z3.discriminatedUnion("kind", [
300
398
  IssueBodySetCallRequestSchema,
301
399
  FlowNodeConfigSetCallRequestSchema,
302
- TemplateNodeConfigSetCallRequestSchema
400
+ TemplateNodeConfigSetCallRequestSchema,
401
+ KanbanWaveDispatchCallRequestSchema,
402
+ IssueSubissueCreateCallRequestSchema,
403
+ IssueCreateCallRequestSchema,
404
+ IssueStateSetCallRequestSchema,
405
+ IssueCommentCreateCallRequestSchema,
406
+ IssueLabelsSetCallRequestSchema
303
407
  ]);
304
408
  var AgentCallResultSchema = z3.object({
305
409
  type: z3.literal("agent-call-result"),
@@ -314,7 +418,8 @@ var ServerToDeviceMessageSchema = z3.discriminatedUnion("type", [
314
418
  JobAssignmentSchema,
315
419
  HelloAckSchema,
316
420
  PingSchema,
317
- AgentCallResultSchema
421
+ AgentCallResultSchema,
422
+ CancelJobSchema
318
423
  ]);
319
424
  var DeviceToServerMessageSchema = z3.union([
320
425
  HelloMessageSchema,
@@ -383,6 +488,42 @@ var templateNodeConfigSetShape = TemplateNodeConfigSetCallSchema.omit({
383
488
  callId: true,
384
489
  kind: true
385
490
  }).shape;
491
+ var kanbanWaveDispatchShape = KanbanWaveDispatchCallSchema.omit({
492
+ type: true,
493
+ runId: true,
494
+ callId: true,
495
+ kind: true
496
+ }).shape;
497
+ var issueSubissueCreateShape = IssueSubissueCreateCallSchema.omit({
498
+ type: true,
499
+ runId: true,
500
+ callId: true,
501
+ kind: true
502
+ }).shape;
503
+ var issueCreateShape = IssueCreateCallSchema.omit({
504
+ type: true,
505
+ runId: true,
506
+ callId: true,
507
+ kind: true
508
+ }).shape;
509
+ var issueStateSetShape = IssueStateSetCallSchema.omit({
510
+ type: true,
511
+ runId: true,
512
+ callId: true,
513
+ kind: true
514
+ }).shape;
515
+ var issueCommentCreateShape = IssueCommentCreateCallSchema.omit({
516
+ type: true,
517
+ runId: true,
518
+ callId: true,
519
+ kind: true
520
+ }).shape;
521
+ var issueLabelsSetShape = IssueLabelsSetCallSchema.omit({
522
+ type: true,
523
+ runId: true,
524
+ callId: true,
525
+ kind: true
526
+ }).shape;
386
527
  var TOOLS = [
387
528
  {
388
529
  name: "opencara_issue_body_set",
@@ -404,6 +545,48 @@ var TOOLS = [
404
545
  title: "Update a flow-template draft node's config",
405
546
  description: "Replace the config blob of a node in the user's draft of the named flow template. Per-user scope, not per-project. Reject with reason if the template draft isn't owned by the run's user.",
406
547
  inputShape: templateNodeConfigSetShape
548
+ },
549
+ {
550
+ name: "opencara_kanban_wave_dispatch",
551
+ kind: "kanban.wave.dispatch",
552
+ title: "Dispatch a batch of issues to a flow",
553
+ description: "Dispatch up to 10 issues in parallel to the named project flow. Requires project scope. Reject if the flow does not exist, is disabled, or any of the issue numbers are not in the project. Returns the wave id.",
554
+ inputShape: kanbanWaveDispatchShape
555
+ },
556
+ {
557
+ name: "opencara_issue_subissue_create",
558
+ kind: "issue.subissue.create",
559
+ title: "Create a GitHub sub-issue under a parent",
560
+ description: "Create a new GitHub issue and link it as a child of the given parent issue via the GraphQL addSubIssue mutation. Requires project scope. Reject if the parent issue is not in the project.",
561
+ inputShape: issueSubissueCreateShape
562
+ },
563
+ {
564
+ name: "opencara_issue_create",
565
+ kind: "issue.create",
566
+ title: "Create a top-level GitHub issue",
567
+ description: "Create a new GitHub issue in the run's project with no parent link. Requires project scope. Returns the new issueNumber and nodeId.",
568
+ inputShape: issueCreateShape
569
+ },
570
+ {
571
+ name: "opencara_issue_state_set",
572
+ kind: "issue.state.set",
573
+ title: "Open or close an existing issue",
574
+ description: "Set an issue's state to open or closed. Optional stateReason: completed | not_planned | reopened. Requires project scope. Reject if the issue is not in the project.",
575
+ inputShape: issueStateSetShape
576
+ },
577
+ {
578
+ name: "opencara_issue_comment_create",
579
+ kind: "issue.comment.create",
580
+ title: "Post a comment on an issue",
581
+ description: "Post a Markdown comment on the named issue. Comments are not mirrored locally; the comment lives on GitHub. Requires project scope. Reject if the issue is not in the project.",
582
+ inputShape: issueCommentCreateShape
583
+ },
584
+ {
585
+ name: "opencara_issue_labels_set",
586
+ kind: "issue.labels.set",
587
+ title: "Replace the label set on an issue",
588
+ description: "Set the issue's labels to exactly the listed names (REST setLabels semantics). Any label not in the list is removed; empty array clears all labels. Requires project scope.",
589
+ inputShape: issueLabelsSetShape
407
590
  }
408
591
  ];
409
592
  function registerOpencaraTools(server, router) {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "opencara",
3
- "version": "0.106.0",
3
+ "version": "0.107.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": {