unique-sdk 2026.32.0.dev13__tar.gz → 2026.32.0.dev14__tar.gz

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.
Files changed (89) hide show
  1. {unique_sdk-2026.32.0.dev13 → unique_sdk-2026.32.0.dev14}/PKG-INFO +1 -1
  2. {unique_sdk-2026.32.0.dev13 → unique_sdk-2026.32.0.dev14}/pyproject.toml +1 -1
  3. {unique_sdk-2026.32.0.dev13 → unique_sdk-2026.32.0.dev14}/unique_sdk/api_resources/_agentic_table.py +32 -0
  4. {unique_sdk-2026.32.0.dev13 → unique_sdk-2026.32.0.dev14}/unique_sdk/cli/cli.py +78 -2
  5. {unique_sdk-2026.32.0.dev13 → unique_sdk-2026.32.0.dev14}/unique_sdk/cli/commands/agentic_table_write.py +83 -0
  6. {unique_sdk-2026.32.0.dev13 → unique_sdk-2026.32.0.dev14}/unique_sdk/cli/skills/unique-cli-agentic-table/SKILL.md +55 -6
  7. {unique_sdk-2026.32.0.dev13 → unique_sdk-2026.32.0.dev14}/README.md +0 -0
  8. {unique_sdk-2026.32.0.dev13 → unique_sdk-2026.32.0.dev14}/unique_sdk/__init__.py +0 -0
  9. {unique_sdk-2026.32.0.dev13 → unique_sdk-2026.32.0.dev14}/unique_sdk/_api_requestor.py +0 -0
  10. {unique_sdk-2026.32.0.dev13 → unique_sdk-2026.32.0.dev14}/unique_sdk/_api_resource.py +0 -0
  11. {unique_sdk-2026.32.0.dev13 → unique_sdk-2026.32.0.dev14}/unique_sdk/_api_version.py +0 -0
  12. {unique_sdk-2026.32.0.dev13 → unique_sdk-2026.32.0.dev14}/unique_sdk/_error.py +0 -0
  13. {unique_sdk-2026.32.0.dev13 → unique_sdk-2026.32.0.dev14}/unique_sdk/_http_client.py +0 -0
  14. {unique_sdk-2026.32.0.dev13 → unique_sdk-2026.32.0.dev14}/unique_sdk/_list_object.py +0 -0
  15. {unique_sdk-2026.32.0.dev13 → unique_sdk-2026.32.0.dev14}/unique_sdk/_object_classes.py +0 -0
  16. {unique_sdk-2026.32.0.dev13 → unique_sdk-2026.32.0.dev14}/unique_sdk/_request_options.py +0 -0
  17. {unique_sdk-2026.32.0.dev13 → unique_sdk-2026.32.0.dev14}/unique_sdk/_unique_object.py +0 -0
  18. {unique_sdk-2026.32.0.dev13 → unique_sdk-2026.32.0.dev14}/unique_sdk/_unique_ql.py +0 -0
  19. {unique_sdk-2026.32.0.dev13 → unique_sdk-2026.32.0.dev14}/unique_sdk/_unique_response.py +0 -0
  20. {unique_sdk-2026.32.0.dev13 → unique_sdk-2026.32.0.dev14}/unique_sdk/_util.py +0 -0
  21. {unique_sdk-2026.32.0.dev13 → unique_sdk-2026.32.0.dev14}/unique_sdk/_version.py +0 -0
  22. {unique_sdk-2026.32.0.dev13 → unique_sdk-2026.32.0.dev14}/unique_sdk/_webhook.py +0 -0
  23. {unique_sdk-2026.32.0.dev13 → unique_sdk-2026.32.0.dev14}/unique_sdk/api_resources/__init__.py +0 -0
  24. {unique_sdk-2026.32.0.dev13 → unique_sdk-2026.32.0.dev14}/unique_sdk/api_resources/_acronyms.py +0 -0
  25. {unique_sdk-2026.32.0.dev13 → unique_sdk-2026.32.0.dev14}/unique_sdk/api_resources/_analytics_order.py +0 -0
  26. {unique_sdk-2026.32.0.dev13 → unique_sdk-2026.32.0.dev14}/unique_sdk/api_resources/_benchmarking.py +0 -0
  27. {unique_sdk-2026.32.0.dev13 → unique_sdk-2026.32.0.dev14}/unique_sdk/api_resources/_briefing.py +0 -0
  28. {unique_sdk-2026.32.0.dev13 → unique_sdk-2026.32.0.dev14}/unique_sdk/api_resources/_chat_completion.py +0 -0
  29. {unique_sdk-2026.32.0.dev13 → unique_sdk-2026.32.0.dev14}/unique_sdk/api_resources/_content.py +0 -0
  30. {unique_sdk-2026.32.0.dev13 → unique_sdk-2026.32.0.dev14}/unique_sdk/api_resources/_elicitation.py +0 -0
  31. {unique_sdk-2026.32.0.dev13 → unique_sdk-2026.32.0.dev14}/unique_sdk/api_resources/_embedding.py +0 -0
  32. {unique_sdk-2026.32.0.dev13 → unique_sdk-2026.32.0.dev14}/unique_sdk/api_resources/_event.py +0 -0
  33. {unique_sdk-2026.32.0.dev13 → unique_sdk-2026.32.0.dev14}/unique_sdk/api_resources/_folder.py +0 -0
  34. {unique_sdk-2026.32.0.dev13 → unique_sdk-2026.32.0.dev14}/unique_sdk/api_resources/_group.py +0 -0
  35. {unique_sdk-2026.32.0.dev13 → unique_sdk-2026.32.0.dev14}/unique_sdk/api_resources/_integrated.py +0 -0
  36. {unique_sdk-2026.32.0.dev13 → unique_sdk-2026.32.0.dev14}/unique_sdk/api_resources/_llm_models.py +0 -0
  37. {unique_sdk-2026.32.0.dev13 → unique_sdk-2026.32.0.dev14}/unique_sdk/api_resources/_mcp.py +0 -0
  38. {unique_sdk-2026.32.0.dev13 → unique_sdk-2026.32.0.dev14}/unique_sdk/api_resources/_message.py +0 -0
  39. {unique_sdk-2026.32.0.dev13 → unique_sdk-2026.32.0.dev14}/unique_sdk/api_resources/_message_assessment.py +0 -0
  40. {unique_sdk-2026.32.0.dev13 → unique_sdk-2026.32.0.dev14}/unique_sdk/api_resources/_message_execution.py +0 -0
  41. {unique_sdk-2026.32.0.dev13 → unique_sdk-2026.32.0.dev14}/unique_sdk/api_resources/_message_log.py +0 -0
  42. {unique_sdk-2026.32.0.dev13 → unique_sdk-2026.32.0.dev14}/unique_sdk/api_resources/_message_tool.py +0 -0
  43. {unique_sdk-2026.32.0.dev13 → unique_sdk-2026.32.0.dev14}/unique_sdk/api_resources/_module.py +0 -0
  44. {unique_sdk-2026.32.0.dev13 → unique_sdk-2026.32.0.dev14}/unique_sdk/api_resources/_scheduled_task.py +0 -0
  45. {unique_sdk-2026.32.0.dev13 → unique_sdk-2026.32.0.dev14}/unique_sdk/api_resources/_search.py +0 -0
  46. {unique_sdk-2026.32.0.dev13 → unique_sdk-2026.32.0.dev14}/unique_sdk/api_resources/_search_string.py +0 -0
  47. {unique_sdk-2026.32.0.dev13 → unique_sdk-2026.32.0.dev14}/unique_sdk/api_resources/_short_term_memory.py +0 -0
  48. {unique_sdk-2026.32.0.dev13 → unique_sdk-2026.32.0.dev14}/unique_sdk/api_resources/_space.py +0 -0
  49. {unique_sdk-2026.32.0.dev13 → unique_sdk-2026.32.0.dev14}/unique_sdk/api_resources/_user.py +0 -0
  50. {unique_sdk-2026.32.0.dev13 → unique_sdk-2026.32.0.dev14}/unique_sdk/api_resources/_web_search.py +0 -0
  51. {unique_sdk-2026.32.0.dev13 → unique_sdk-2026.32.0.dev14}/unique_sdk/cli/__init__.py +0 -0
  52. {unique_sdk-2026.32.0.dev13 → unique_sdk-2026.32.0.dev14}/unique_sdk/cli/__main__.py +0 -0
  53. {unique_sdk-2026.32.0.dev13 → unique_sdk-2026.32.0.dev14}/unique_sdk/cli/commands/__init__.py +0 -0
  54. {unique_sdk-2026.32.0.dev13 → unique_sdk-2026.32.0.dev14}/unique_sdk/cli/commands/_citation_manifest.py +0 -0
  55. {unique_sdk-2026.32.0.dev13 → unique_sdk-2026.32.0.dev14}/unique_sdk/cli/commands/agentic_table.py +0 -0
  56. {unique_sdk-2026.32.0.dev13 → unique_sdk-2026.32.0.dev14}/unique_sdk/cli/commands/browser.py +0 -0
  57. {unique_sdk-2026.32.0.dev13 → unique_sdk-2026.32.0.dev14}/unique_sdk/cli/commands/cite_file.py +0 -0
  58. {unique_sdk-2026.32.0.dev13 → unique_sdk-2026.32.0.dev14}/unique_sdk/cli/commands/elicitation.py +0 -0
  59. {unique_sdk-2026.32.0.dev13 → unique_sdk-2026.32.0.dev14}/unique_sdk/cli/commands/files.py +0 -0
  60. {unique_sdk-2026.32.0.dev13 → unique_sdk-2026.32.0.dev14}/unique_sdk/cli/commands/folders.py +0 -0
  61. {unique_sdk-2026.32.0.dev13 → unique_sdk-2026.32.0.dev14}/unique_sdk/cli/commands/mcp.py +0 -0
  62. {unique_sdk-2026.32.0.dev13 → unique_sdk-2026.32.0.dev14}/unique_sdk/cli/commands/navigation.py +0 -0
  63. {unique_sdk-2026.32.0.dev13 → unique_sdk-2026.32.0.dev14}/unique_sdk/cli/commands/read.py +0 -0
  64. {unique_sdk-2026.32.0.dev13 → unique_sdk-2026.32.0.dev14}/unique_sdk/cli/commands/scheduled_tasks.py +0 -0
  65. {unique_sdk-2026.32.0.dev13 → unique_sdk-2026.32.0.dev14}/unique_sdk/cli/commands/search.py +0 -0
  66. {unique_sdk-2026.32.0.dev13 → unique_sdk-2026.32.0.dev14}/unique_sdk/cli/commands/subagent.py +0 -0
  67. {unique_sdk-2026.32.0.dev13 → unique_sdk-2026.32.0.dev14}/unique_sdk/cli/commands/web_search.py +0 -0
  68. {unique_sdk-2026.32.0.dev13 → unique_sdk-2026.32.0.dev14}/unique_sdk/cli/commands/web_search_config.py +0 -0
  69. {unique_sdk-2026.32.0.dev13 → unique_sdk-2026.32.0.dev14}/unique_sdk/cli/config.py +0 -0
  70. {unique_sdk-2026.32.0.dev13 → unique_sdk-2026.32.0.dev14}/unique_sdk/cli/formatting.py +0 -0
  71. {unique_sdk-2026.32.0.dev13 → unique_sdk-2026.32.0.dev14}/unique_sdk/cli/identity.py +0 -0
  72. {unique_sdk-2026.32.0.dev13 → unique_sdk-2026.32.0.dev14}/unique_sdk/cli/metadata_filter.py +0 -0
  73. {unique_sdk-2026.32.0.dev13 → unique_sdk-2026.32.0.dev14}/unique_sdk/cli/shell.py +0 -0
  74. {unique_sdk-2026.32.0.dev13 → unique_sdk-2026.32.0.dev14}/unique_sdk/cli/skills/unique-cli-elicitation/SKILL.md +0 -0
  75. {unique_sdk-2026.32.0.dev13 → unique_sdk-2026.32.0.dev14}/unique_sdk/cli/skills/unique-cli-file-management/SKILL.md +0 -0
  76. {unique_sdk-2026.32.0.dev13 → unique_sdk-2026.32.0.dev14}/unique_sdk/cli/skills/unique-cli-mcp/SKILL.md +0 -0
  77. {unique_sdk-2026.32.0.dev13 → unique_sdk-2026.32.0.dev14}/unique_sdk/cli/skills/unique-cli-scheduled-tasks/SKILL.md +0 -0
  78. {unique_sdk-2026.32.0.dev13 → unique_sdk-2026.32.0.dev14}/unique_sdk/cli/skills/unique-cli-search/SKILL.md +0 -0
  79. {unique_sdk-2026.32.0.dev13 → unique_sdk-2026.32.0.dev14}/unique_sdk/cli/skills/unique-cli-subagent/SKILL.md +0 -0
  80. {unique_sdk-2026.32.0.dev13 → unique_sdk-2026.32.0.dev14}/unique_sdk/cli/skills/unique-cli-uploaded-search/SKILL.md +0 -0
  81. {unique_sdk-2026.32.0.dev13 → unique_sdk-2026.32.0.dev14}/unique_sdk/cli/skills/unique-cli-web-search/SKILL.md +0 -0
  82. {unique_sdk-2026.32.0.dev13 → unique_sdk-2026.32.0.dev14}/unique_sdk/cli/state.py +0 -0
  83. {unique_sdk-2026.32.0.dev13 → unique_sdk-2026.32.0.dev14}/unique_sdk/utils/analytics_order_run.py +0 -0
  84. {unique_sdk-2026.32.0.dev13 → unique_sdk-2026.32.0.dev14}/unique_sdk/utils/benchmarking_run.py +0 -0
  85. {unique_sdk-2026.32.0.dev13 → unique_sdk-2026.32.0.dev14}/unique_sdk/utils/chat_history.py +0 -0
  86. {unique_sdk-2026.32.0.dev13 → unique_sdk-2026.32.0.dev14}/unique_sdk/utils/chat_in_space.py +0 -0
  87. {unique_sdk-2026.32.0.dev13 → unique_sdk-2026.32.0.dev14}/unique_sdk/utils/file_io.py +0 -0
  88. {unique_sdk-2026.32.0.dev13 → unique_sdk-2026.32.0.dev14}/unique_sdk/utils/sources.py +0 -0
  89. {unique_sdk-2026.32.0.dev13 → unique_sdk-2026.32.0.dev14}/unique_sdk/utils/token.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.3
2
2
  Name: unique-sdk
3
- Version: 2026.32.0.dev13
3
+ Version: 2026.32.0.dev14
4
4
  Summary:
5
5
  Author: Martin Fadler, Konstantin Krauss, Andreas Hauri
6
6
  Author-email: Martin Fadler <martin.fadler@unique.ch>, Konstantin Krauss <konstantin@unique.ch>, Andreas Hauri <andreas@unique.ch>
@@ -1,6 +1,6 @@
1
1
  [project]
2
2
  name = "unique_sdk"
3
- version = "2026.32.0.dev13"
3
+ version = "2026.32.0.dev14"
4
4
  description = ""
5
5
  readme = "README.md"
6
6
  license = { text = "MIT" }
@@ -600,6 +600,38 @@ class AgenticTable(APIResource["AgenticTable"]):
600
600
  ),
601
601
  )
602
602
 
603
+ @classmethod
604
+ async def rerun_row(
605
+ cls,
606
+ user_id: str,
607
+ company_id: str,
608
+ tableId: str,
609
+ rowOrder: int,
610
+ ) -> MagicTableActionResult:
611
+ """Re-run the agent for a single row (`POST /magic-table/{tableId}/row/{rowOrder}/rerun`).
612
+
613
+ The only way to re-answer part of a sheet: importing questions is
614
+ delta-based, so it will not re-answer a row that already exists.
615
+
616
+ Asynchronous, like the whole-sheet run — the sheet enters ``PROCESSING``
617
+ and this returns once the trigger is accepted.
618
+
619
+ ``rowOrder`` is the sheet's own row numbering, the same one ``get_cell``
620
+ takes: row 0 is the header, so answerable rows start at 1 and row 0 is
621
+ rejected by the API.
622
+ """
623
+ url = f"/magic-table/{tableId}/row/{rowOrder}/rerun"
624
+ return cast(
625
+ MagicTableActionResult,
626
+ await cls._static_request_async(
627
+ "post",
628
+ url,
629
+ user_id,
630
+ company_id,
631
+ {},
632
+ ),
633
+ )
634
+
603
635
  @classmethod
604
636
  async def list_artifacts(
605
637
  cls,
@@ -22,6 +22,7 @@ from unique_sdk.cli.commands.agentic_table_write import (
22
22
  cmd_create_sheet,
23
23
  cmd_export,
24
24
  cmd_import,
25
+ cmd_rerun_row,
25
26
  )
26
27
  from unique_sdk.cli.commands.browser import (
27
28
  cmd_browser_action,
@@ -2212,8 +2213,9 @@ Work with Agentic Table (magic table) sheets over the public magic-table API.
2212
2213
 
2213
2214
  \b
2214
2215
  Reads (get-sheet / get-cell / cell-history / list-exports) are Tier 0: no
2215
- confirmation, no side effects. Writes (create-sheet / import / export) build
2216
- and run a sheet — the full loop of create, populate, run, and export. Every
2216
+ confirmation, no side effects. Writes (create-sheet / import / rerun-row /
2217
+ export) build and run a sheet — the full loop of create, populate, run, and
2218
+ export, plus re-answering a single row. Every
2217
2219
  call is scoped to the current user/company; sheet-role access (Owner / Can
2218
2220
  manage / Can edit) is enforced server-side and a denial is reported as
2219
2221
  `agentic-table: permission denied`.
@@ -2229,6 +2231,7 @@ Read subcommands:
2229
2231
  Write subcommands:
2230
2232
  create-sheet Create a new sheet in a space
2231
2233
  import Import questions/sources (adding questions triggers the run)
2234
+ rerun-row Re-run the agent for a single row (import cannot redo a row)
2232
2235
  export Generate export artifacts (report / question export)
2233
2236
 
2234
2237
  \b
@@ -2544,6 +2547,79 @@ def agentic_table_import(
2544
2547
  )
2545
2548
 
2546
2549
 
2550
+ @agentic_table.command(name="rerun-row")
2551
+ @click.argument("table_id")
2552
+ # Rejected locally rather than by the endpoint: a rerun is an audited write, so
2553
+ # a row number that cannot refer to an answerable row would otherwise cost a
2554
+ # round trip and leave an audit entry for input the client could see was wrong.
2555
+ @click.argument("row_order", type=click.IntRange(min=1))
2556
+ @click.option(
2557
+ "--wait",
2558
+ "wait",
2559
+ is_flag=True,
2560
+ default=False,
2561
+ help="Wait for the triggered rerun to finish before returning.",
2562
+ )
2563
+ @click.option(
2564
+ "--timeout",
2565
+ "timeout",
2566
+ type=click.FloatRange(min=0),
2567
+ default=600.0,
2568
+ show_default=True,
2569
+ help="Max seconds to wait when --wait is set.",
2570
+ )
2571
+ @click.option(
2572
+ "--start-timeout",
2573
+ "start_timeout",
2574
+ type=click.FloatRange(min=0),
2575
+ default=120.0,
2576
+ show_default=True,
2577
+ help=(
2578
+ "Max seconds to wait for the rerun to be picked up before treating it "
2579
+ "as never started. Raise it when the worker queue is slow."
2580
+ ),
2581
+ )
2582
+ @click.option(
2583
+ "--json", "output_json", is_flag=True, default=False, help="Print raw JSON."
2584
+ )
2585
+ @click.pass_context
2586
+ def agentic_table_rerun_row(
2587
+ ctx: click.Context,
2588
+ table_id: str,
2589
+ row_order: int,
2590
+ wait: bool,
2591
+ timeout: float,
2592
+ start_timeout: float,
2593
+ output_json: bool,
2594
+ ) -> None:
2595
+ """Re-run the agent for a single row.
2596
+
2597
+ \b
2598
+ Use this to redo one answer: import is delta-based and skips questions the
2599
+ sheet already has, so it cannot re-answer an existing row. ROW_ORDER is the
2600
+ same number get-cell --row takes: row 0 is the header, so answerable rows
2601
+ start at 1. A locked or final row is rejected; so is a rerun while the sheet
2602
+ is already processing, which clears once the current run finishes.
2603
+
2604
+ \b
2605
+ Examples:
2606
+ unique-cli agentic-table rerun-row mt_abc123 4
2607
+ unique-cli agentic-table rerun-row mt_abc123 4 --wait
2608
+ """
2609
+ emit(
2610
+ cmd_rerun_row(
2611
+ LazyState.get(ctx),
2612
+ table_id,
2613
+ row_order,
2614
+ wait=wait,
2615
+ timeout=timeout,
2616
+ start_timeout=start_timeout,
2617
+ output_json=output_json,
2618
+ ),
2619
+ is_error=_is_agentic_table_error_output,
2620
+ )
2621
+
2622
+
2547
2623
  @agentic_table.command(name="export")
2548
2624
  @click.argument("table_id")
2549
2625
  @click.option(
@@ -4,6 +4,7 @@ The full-loop write slice over the public magic-table API (``2023-12-06``):
4
4
 
5
5
  - ``create-sheet`` — create an empty sheet in a space.
6
6
  - ``import`` — add questions/sources; adding new questions triggers the agent run.
7
+ - ``rerun-row`` — re-run the agent for a single row.
7
8
  - ``export`` — generate export artifacts (report / question export) and list them.
8
9
 
9
10
  Together these let an agent build a sheet, run it, and collect the answers it
@@ -288,6 +289,88 @@ def cmd_import(
288
289
  return _error(exc)
289
290
 
290
291
 
292
+ def cmd_rerun_row(
293
+ state: ShellState,
294
+ table_id: str,
295
+ row_order: int,
296
+ *,
297
+ wait: bool = False,
298
+ timeout: float = _DEFAULT_WAIT_TIMEOUT_SECONDS,
299
+ start_timeout: float = _RUN_START_TIMEOUT_SECONDS,
300
+ output_json: bool = False,
301
+ ) -> str:
302
+ """Re-run the agent for one row (``POST .../row/{rowOrder}/rerun``).
303
+
304
+ The only way to re-answer part of a sheet: ``import`` is delta-based and
305
+ skips questions the sheet already has, so it cannot redo an existing row.
306
+
307
+ ``row_order`` is the same number ``get-cell --row`` takes — row 0 is the
308
+ header, so answerable rows start at 1 — and the CLI rejects anything below
309
+ that before the request is made. The backend declines a row that is locked
310
+ or in a final review status, and any rerun while the sheet is already
311
+ ``PROCESSING``.
312
+
313
+ Unlike ``import``, a rerun always triggers a run, so there is no benign
314
+ "nothing started" case: with ``wait``, not observing one is an error.
315
+
316
+ The wait watches *sheet* state, which on a shared sheet cannot be attributed
317
+ to this row's rerun specifically: a run someone else started looks the same.
318
+ A row-level signal would fix that (UN-23683).
319
+ """
320
+
321
+ async def _run() -> str:
322
+ result = await AgenticTable.rerun_row(
323
+ user_id=state.config.user_id,
324
+ company_id=state.config.company_id,
325
+ tableId=table_id,
326
+ rowOrder=row_order,
327
+ )
328
+ if not result.get("status"):
329
+ return _rejected(result, action="rerun")
330
+ if output_json and not wait:
331
+ return json.dumps({"result": result}, indent=2, default=str)
332
+ if not wait:
333
+ return format_agentic_table_action_result(result, action="rerun")
334
+
335
+ started, final_state, timed_out = await _wait_for_run(
336
+ state, table_id, timeout=timeout, start_timeout=start_timeout
337
+ )
338
+ if timed_out:
339
+ return (
340
+ f"{AGENTIC_TABLE_ERROR_PREFIX} timed out after {timeout:g}s waiting "
341
+ f"for the row {row_order} rerun to finish (sheet {table_id} still "
342
+ "PROCESSING)"
343
+ )
344
+ if not started:
345
+ window = min(start_timeout, timeout)
346
+ return (
347
+ f"{AGENTIC_TABLE_ERROR_PREFIX} rerun of row {row_order} on sheet "
348
+ f"{table_id} was accepted but no run was observed within "
349
+ f"{window:g}s (state: {final_state}); it may have finished between "
350
+ "polls or may still be waiting to be picked up, so read the row "
351
+ "back with cell-history to see whether it was re-answered"
352
+ )
353
+ if output_json:
354
+ return json.dumps(
355
+ {
356
+ "result": result,
357
+ "rowOrder": row_order,
358
+ "finalState": str(final_state) if final_state else None,
359
+ },
360
+ indent=2,
361
+ default=str,
362
+ )
363
+ return (
364
+ f"{format_agentic_table_action_result(result, action='rerun')}\n"
365
+ f"Row {row_order} rerun finished (state: {final_state})."
366
+ )
367
+
368
+ try:
369
+ return asyncio.run(_run())
370
+ except UniqueError as exc:
371
+ return _error(exc)
372
+
373
+
291
374
  def cmd_export(
292
375
  state: ShellState,
293
376
  table_id: str,
@@ -21,11 +21,17 @@ Commands fall into two groups:
21
21
 
22
22
  - **Read (Tier 0)** — `get-sheet`, `get-cell`, `cell-history`, `list-exports`.
23
23
  Never modify anything, never need confirmation.
24
- - **Write (Tier 1)** — `create-sheet`, `import`, `export`. These create a sheet,
25
- add questions and sources, start the agent run, and produce export artifacts.
26
- They add to a sheet rather than overwriting existing answers, so they do not
27
- prompt for confirmation — but they do change a shared artifact, so say what
28
- you did afterwards. Nothing here deletes or replaces a human's answer.
24
+ - **Write (Tier 1)** — `create-sheet`, `import`, `export`, `rerun-row`. These
25
+ create a sheet, add questions and sources, start the agent run, produce
26
+ export artifacts, and redo a single answer. None of them prompts for
27
+ confirmation, but they do change a shared artifact, so say what you did
28
+ afterwards.
29
+
30
+ The first three only add. `rerun-row` is the exception: it replaces the
31
+ answer in the row you name. The previous answer stays in `cell-history` and
32
+ a settled row is refused outright, so the change is recoverable and the
33
+ approved rows are protected — but name the row you are redoing when you
34
+ report back, and check you have the right one first.
29
35
 
30
36
  ## Permissions
31
37
 
@@ -156,6 +162,42 @@ returns the same error.
156
162
  unique-cli agentic-table import mt_abc123 --question-file-id c_q --source-file-id c_src --wait
157
163
  ```
158
164
 
165
+ ### Re-answer a single row
166
+
167
+ ```bash
168
+ unique-cli agentic-table rerun-row <table_id> <row_order> [--wait] [--timeout <seconds>] [--start-timeout <seconds>]
169
+ ```
170
+
171
+ Use this to redo one answer. **Re-importing a question will not redo it** —
172
+ import is delta-based and skips questions the sheet already has, so `rerun-row`
173
+ is the only way to re-answer an existing row.
174
+
175
+ `<row_order>` uses **the same numbering as `--row` on `get-cell`**: row 0 is the
176
+ header, data rows start at 1. So the row you inspected with
177
+ `get-cell --row 4` is the row you redo with `rerun-row <table_id> 4` — no
178
+ offset. Row 0 is rejected, since there is nothing to answer in a header.
179
+
180
+ Two kinds of refusal, which need different responses:
181
+
182
+ - **The row is not yours to redo.** A locked row, or one in a final review
183
+ status, is declined because a person has settled that answer. Report it; do
184
+ not look for a way around it.
185
+ - **Not now.** A rerun while the sheet is already processing is declined
186
+ because only one run may be in flight. That clears by itself — wait for the
187
+ current run to finish and try again.
188
+
189
+ A rerun always starts a run, so with `--wait` there is no benign "nothing
190
+ started" case: if no run appears within 120s the command fails.
191
+
192
+ ```bash
193
+ unique-cli agentic-table rerun-row mt_abc123 4 --wait
194
+ ```
195
+
196
+ To redo several rows, do them one at a time with `--wait`: the sheet takes one
197
+ run at a time, so a second `rerun-row` fired before the first finishes is
198
+ declined. There is no batch form. If most of the sheet needs redoing, consider
199
+ a fresh sheet instead.
200
+
159
201
  ### Export answers
160
202
 
161
203
  ```bash
@@ -195,9 +237,16 @@ answered, skip the create and import steps: read the answers with
195
237
  `get-sheet --cells` or `get-cell`, and use `cell-history` if you need to know
196
238
  whether an answer came from a person or the assistant.
197
239
 
240
+ If a specific answer looks wrong or incomplete, fix that row with `rerun-row`
241
+ and export again, rather than re-importing the question or rebuilding the
242
+ sheet.
243
+
198
244
  ## Rules
199
245
 
200
- 1. `--row` and `--col` are **0-based** orders. Row 0 is the header row.
246
+ 1. Rows and columns are numbered from 0, and row 0 is the header — so the first
247
+ question is row 1. This holds for `--row`/`--col` on `get-cell` and
248
+ `cell-history` and for `<row_order>` on `rerun-row` alike; the same number
249
+ means the same row in every command.
201
250
  2. Fetch what you need, not everything. `get-cell` for one value,
202
251
  `get-sheet --cells` for an overview — don't dump a whole sheet unless asked.
203
252
  3. Use `--wait` when a later step depends on the result, and only then. Without