@aryaminus/controlkeel-opencode 0.2.44 → 0.2.46

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.
@@ -10,7 +10,7 @@ Recommended flow:
10
10
  3. Run `controlkeel review plan submit --body-file .opencode/review-plan.md --submitted-by opencode --task-id <task_id> --json` (or use `--session-id <session_id>`)
11
11
  4. Read the returned `review.id` and `browser_url`
12
12
  5. If `browser_url` is available, wait with `controlkeel review plan wait --id <review_id> --timeout 30 --json`
13
- 6. If `browser_url` is missing/unreachable **or** wait times out while still `pending`, do **not** loop on wait; ask for explicit user approval in chat and record it with `controlkeel review plan respond --id <review_id> --decision approved --feedback-notes "User approved in chat; browser unavailable or timed out" --json` (or `ck_review_feedback`)
13
+ 6. If `browser_url` is missing/unreachable, the browser does not actually open, **or** wait times out while still `pending`, do **not** loop on wait; ask for explicit user approval in chat and record it with `controlkeel review plan respond --id <review_id> --decision approved --feedback-notes "User approved in chat; browser unavailable or timed out" --json` (or `ck_review_feedback`)
14
14
  7. Do not execute until the review is approved
15
15
 
16
16
  Fallback when the `submit_plan` tool is stale in a long-running OpenCode session:
@@ -343,29 +343,42 @@ export const ControlKeelGovernance: Plugin = async ({ project, client, $, direct
343
343
  submitPayload?.review?.browser_url ??
344
344
  null
345
345
 
346
+ const buildPlanResult = (overrides = {}) => ({
347
+ reviewId,
348
+ browserUrl: overrides.browserUrl ?? browserUrl,
349
+ status: overrides.status ?? submitPayload?.review?.status ?? "pending",
350
+ feedbackNotes:
351
+ overrides.feedbackNotes ?? submitPayload?.review?.feedback_notes ?? null,
352
+ opened: overrides.opened ?? (openPayload?.opened === true),
353
+ timedOut: overrides.timedOut ?? false,
354
+ waitSkipped: overrides.waitSkipped ?? false,
355
+ manualApprovalRequired: overrides.manualApprovalRequired ?? false,
356
+ reason: overrides.reason ?? null,
357
+ guidance: overrides.guidance ?? null,
358
+ })
359
+
346
360
  const openError = typeof openPayload?.open_error === "string" ? openPayload.open_error.trim() : ""
347
361
  const openFailure = typeof openPayload?.error === "string" ? openPayload.error.trim() : ""
362
+ const browserNotOpened = openPayload?.opened !== true
348
363
 
349
364
  const remoteLocalhostMismatch =
350
365
  typeof browserUrl === "string" &&
351
366
  browserUrl.includes("localhost") &&
352
367
  openPayload?.remote === true
353
368
 
354
- if (!browserUrl || openError || openFailure || remoteLocalhostMismatch) {
355
- return {
356
- reviewId,
357
- submitPayload,
358
- openPayload,
359
- browserUrl,
360
- status: submitPayload?.review?.status ?? "pending",
361
- feedbackNotes: submitPayload?.review?.feedback_notes ?? null,
362
- timedOut: false,
369
+ if (!browserUrl || openError || openFailure || remoteLocalhostMismatch || browserNotOpened) {
370
+ return buildPlanResult({
363
371
  waitSkipped: true,
364
372
  manualApprovalRequired: true,
365
- reason: !browserUrl ? "browser_url_unavailable" : "browser_unreachable",
373
+ reason:
374
+ !browserUrl
375
+ ? "browser_url_unavailable"
376
+ : browserNotOpened
377
+ ? "browser_not_opened"
378
+ : "browser_unreachable",
366
379
  guidance:
367
- "Browser review is unavailable from this environment. Ask the user for explicit approval in chat, then record it with `controlkeel review plan respond --id <review_id> --decision approved --feedback-notes \"User approved in chat; browser unavailable\" --json` or `ck_review_feedback`.",
368
- }
380
+ "Browser review is unavailable from this environment or did not actually open. Ask the user for explicit approval in chat, then record it with `controlkeel review plan respond --id <review_id> --decision approved --feedback-notes \"User approved in chat; browser unavailable\" --json` or `ck_review_feedback`.",
381
+ })
369
382
  }
370
383
 
371
384
  const waitEnv = process.env.LOGGER_LEVEL
@@ -388,10 +401,7 @@ export const ControlKeelGovernance: Plugin = async ({ project, client, $, direct
388
401
 
389
402
  if (waitExit !== 0) {
390
403
  if (waitTimedOut && waitPending) {
391
- return {
392
- reviewId,
393
- submitPayload,
394
- waitPayload,
404
+ return buildPlanResult({
395
405
  browserUrl: waitPayload?.browser_url ?? submitPayload?.browser_url,
396
406
  status: "pending",
397
407
  feedbackNotes: waitPayload?.review?.feedback_notes ?? null,
@@ -401,7 +411,7 @@ export const ControlKeelGovernance: Plugin = async ({ project, client, $, direct
401
411
  reason: "review_timeout",
402
412
  guidance:
403
413
  "Plan review is still pending after timeout. Show the `browser_url` to the user if reachable. If browser review is unavailable or the user explicitly approves in chat, record it with `controlkeel review plan respond --id <review_id> --decision approved --feedback-notes \"User approved in chat after timeout/browser issue\" --json` (or `ck_review_feedback`) before proceeding.",
404
- }
414
+ })
405
415
  }
406
416
 
407
417
  throw new Error(
@@ -409,17 +419,13 @@ export const ControlKeelGovernance: Plugin = async ({ project, client, $, direct
409
419
  )
410
420
  }
411
421
 
412
- return {
413
- reviewId,
414
- submitPayload,
415
- openPayload,
416
- waitPayload,
422
+ return buildPlanResult({
417
423
  browserUrl: waitPayload?.browser_url ?? browserUrl,
418
424
  status: waitPayload?.review?.status,
419
425
  feedbackNotes: waitPayload?.review?.feedback_notes ?? null,
420
426
  waitSkipped: false,
421
427
  manualApprovalRequired: false,
422
- }
428
+ })
423
429
  } finally {
424
430
  // Clean up temp file
425
431
  try { await Bun.file(tmpFile).unlink?.() ?? (await $`rm -f ${tmpFile}`.quiet()) } catch {}
@@ -479,7 +485,7 @@ export const ControlKeelGovernance: Plugin = async ({ project, client, $, direct
479
485
  args.task_id,
480
486
  args.session_id
481
487
  )
482
- return JSON.stringify(result, null, 2)
488
+ return JSON.stringify(result)
483
489
  },
484
490
  }),
485
491
  },
package/index.js CHANGED
@@ -326,29 +326,42 @@ export const ControlKeelGovernance = async ({ $, directory }) => {
326
326
  submitPayload?.review?.browser_url ??
327
327
  null
328
328
 
329
+ const buildPlanResult = (overrides = {}) => ({
330
+ reviewId,
331
+ browserUrl: overrides.browserUrl ?? browserUrl,
332
+ status: overrides.status ?? submitPayload?.review?.status ?? "pending",
333
+ feedbackNotes:
334
+ overrides.feedbackNotes ?? submitPayload?.review?.feedback_notes ?? null,
335
+ opened: overrides.opened ?? (openPayload?.opened === true),
336
+ timedOut: overrides.timedOut ?? false,
337
+ waitSkipped: overrides.waitSkipped ?? false,
338
+ manualApprovalRequired: overrides.manualApprovalRequired ?? false,
339
+ reason: overrides.reason ?? null,
340
+ guidance: overrides.guidance ?? null,
341
+ })
342
+
329
343
  const openError = typeof openPayload?.open_error === "string" ? openPayload.open_error.trim() : ""
330
344
  const openFailure = typeof openPayload?.error === "string" ? openPayload.error.trim() : ""
345
+ const browserNotOpened = openPayload?.opened !== true
331
346
 
332
347
  const remoteLocalhostMismatch =
333
348
  typeof browserUrl === "string" &&
334
349
  browserUrl.includes("localhost") &&
335
350
  openPayload?.remote === true
336
351
 
337
- if (!browserUrl || openError || openFailure || remoteLocalhostMismatch) {
338
- return {
339
- reviewId,
340
- submitPayload,
341
- openPayload,
342
- browserUrl,
343
- status: submitPayload?.review?.status ?? "pending",
344
- feedbackNotes: submitPayload?.review?.feedback_notes ?? null,
345
- timedOut: false,
352
+ if (!browserUrl || openError || openFailure || remoteLocalhostMismatch || browserNotOpened) {
353
+ return buildPlanResult({
346
354
  waitSkipped: true,
347
355
  manualApprovalRequired: true,
348
- reason: !browserUrl ? "browser_url_unavailable" : "browser_unreachable",
356
+ reason:
357
+ !browserUrl
358
+ ? "browser_url_unavailable"
359
+ : browserNotOpened
360
+ ? "browser_not_opened"
361
+ : "browser_unreachable",
349
362
  guidance:
350
- "Browser review is unavailable from this environment. Ask the user for explicit approval in chat, then record it with `controlkeel review plan respond --id <review_id> --decision approved --feedback-notes \"User approved in chat; browser unavailable\" --json` or `ck_review_feedback`.",
351
- }
363
+ "Browser review is unavailable from this environment or did not actually open. Ask the user for explicit approval in chat, then record it with `controlkeel review plan respond --id <review_id> --decision approved --feedback-notes \"User approved in chat; browser unavailable\" --json` or `ck_review_feedback`.",
364
+ })
352
365
  }
353
366
 
354
367
  const waitEnv = process.env.LOGGER_LEVEL
@@ -371,10 +384,7 @@ export const ControlKeelGovernance = async ({ $, directory }) => {
371
384
 
372
385
  if (waitExit !== 0) {
373
386
  if (waitTimedOut && waitPending) {
374
- return {
375
- reviewId,
376
- submitPayload,
377
- waitPayload,
387
+ return buildPlanResult({
378
388
  browserUrl: waitPayload?.browser_url ?? submitPayload?.browser_url,
379
389
  status: "pending",
380
390
  feedbackNotes: waitPayload?.review?.feedback_notes ?? null,
@@ -384,7 +394,7 @@ export const ControlKeelGovernance = async ({ $, directory }) => {
384
394
  reason: "review_timeout",
385
395
  guidance:
386
396
  "Plan review is still pending after timeout. Show the `browser_url` to the user if reachable. If browser review is unavailable or the user explicitly approves in chat, record it with `controlkeel review plan respond --id <review_id> --decision approved --feedback-notes \"User approved in chat after timeout/browser issue\" --json` (or `ck_review_feedback`) before proceeding.",
387
- }
397
+ })
388
398
  }
389
399
 
390
400
  throw new Error(
@@ -392,17 +402,13 @@ export const ControlKeelGovernance = async ({ $, directory }) => {
392
402
  )
393
403
  }
394
404
 
395
- return {
396
- reviewId,
397
- submitPayload,
398
- openPayload,
399
- waitPayload,
405
+ return buildPlanResult({
400
406
  browserUrl: waitPayload?.browser_url ?? browserUrl,
401
407
  status: waitPayload?.review?.status,
402
408
  feedbackNotes: waitPayload?.review?.feedback_notes ?? null,
403
409
  waitSkipped: false,
404
410
  manualApprovalRequired: false,
405
- }
411
+ })
406
412
  } finally {
407
413
  // Clean up temp file
408
414
  try { await Bun.file(tmpFile).unlink?.() ?? (await $`rm -f ${tmpFile}`.quiet()) } catch {}
@@ -462,7 +468,7 @@ export const ControlKeelGovernance = async ({ $, directory }) => {
462
468
  args.task_id,
463
469
  args.session_id
464
470
  )
465
- return JSON.stringify(result, null, 2)
471
+ return JSON.stringify(result)
466
472
  },
467
473
  }),
468
474
  },
package/package.json CHANGED
@@ -35,5 +35,5 @@
35
35
  "url": "git+https://github.com/aryaminus/controlkeel.git"
36
36
  },
37
37
  "type": "module",
38
- "version": "0.2.44"
38
+ "version": "0.2.46"
39
39
  }