Add permission-aware follow-up flow with interactive resolution

When follow-up agent detects permission-blocked items ("ручное
применение", "permission denied", etc.), they become pending_actions
instead of auto-created tasks. User chooses per item:
  1. Rerun with --dangerously-skip-permissions
  2. Create manual task
  3. Skip

core/followup.py:
  _is_permission_blocked() — regex detection of 9 permission patterns
  generate_followups() returns {created, pending_actions}
  resolve_pending_action() — handles rerun/manual_task/skip

agents/runner.py:
  _run_claude(allow_write=True) adds --dangerously-skip-permissions
  run_agent/run_pipeline pass allow_write through

CLI: kin approve --followup — interactive 1/2/3 prompt per blocked item
API: POST /approve returns {needs_decision, pending_actions}
     POST /resolve resolves individual actions
Frontend: pending actions shown as cards with 3 buttons in approve modal

136 tests, all passing. Frontend builds clean.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
johnfrum1234 2026-03-15 15:16:48 +02:00
parent 9264415776
commit ab693d3c4d
7 changed files with 356 additions and 73 deletions

View file

@ -421,7 +421,7 @@ def cost(ctx, period):
@click.pass_context
def approve_task(ctx, task_id, followup, decision_text):
"""Approve a task (set status=done). Optionally generate follow-ups."""
from core.followup import generate_followups
from core.followup import generate_followups, resolve_pending_action
conn = ctx.obj["conn"]
task = models.get_task(conn, task_id)
@ -441,12 +441,36 @@ def approve_task(ctx, task_id, followup, decision_text):
if followup:
click.echo("Generating follow-up tasks...")
created = generate_followups(conn, task_id)
result = generate_followups(conn, task_id)
created = result["created"]
pending = result["pending_actions"]
if created:
click.echo(f"Created {len(created)} follow-up tasks:")
for t in created:
click.echo(f" {t['id']}: {t['title']} (pri {t['priority']})")
else:
for action in pending:
click.echo(f"\nPermission issue: {action['description']}")
click.echo(" 1. Rerun with --dangerously-skip-permissions")
click.echo(" 2. Create task for manual fix")
click.echo(" 3. Skip")
choice_input = click.prompt("Choice", type=click.Choice(["1", "2", "3"]), default="2")
choice_map = {"1": "rerun", "2": "manual_task", "3": "skip"}
choice = choice_map[choice_input]
result = resolve_pending_action(conn, task_id, action, choice)
if choice == "rerun" and result:
rr = result.get("rerun_result", {})
if rr.get("success"):
click.echo(" Re-run completed successfully.")
else:
click.echo(f" Re-run failed: {rr.get('error', 'unknown')}")
elif choice == "manual_task" and result:
click.echo(f" Created: {result['id']}: {result['title']}")
elif choice == "skip":
click.echo(" Skipped.")
if not created and not pending:
click.echo("No follow-up tasks generated.")