deepy-cli 0.2.4__tar.gz → 0.2.5__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 (86) hide show
  1. {deepy_cli-0.2.4 → deepy_cli-0.2.5}/PKG-INFO +1 -1
  2. {deepy_cli-0.2.4 → deepy_cli-0.2.5}/pyproject.toml +1 -1
  3. {deepy_cli-0.2.4 → deepy_cli-0.2.5}/src/deepy/__init__.py +1 -1
  4. {deepy_cli-0.2.4 → deepy_cli-0.2.5}/src/deepy/llm/runner.py +149 -0
  5. {deepy_cli-0.2.4 → deepy_cli-0.2.5}/README.md +0 -0
  6. {deepy_cli-0.2.4 → deepy_cli-0.2.5}/src/deepy/__main__.py +0 -0
  7. {deepy_cli-0.2.4 → deepy_cli-0.2.5}/src/deepy/cli.py +0 -0
  8. {deepy_cli-0.2.4 → deepy_cli-0.2.5}/src/deepy/config/__init__.py +0 -0
  9. {deepy_cli-0.2.4 → deepy_cli-0.2.5}/src/deepy/config/settings.py +0 -0
  10. {deepy_cli-0.2.4 → deepy_cli-0.2.5}/src/deepy/data/__init__.py +0 -0
  11. {deepy_cli-0.2.4 → deepy_cli-0.2.5}/src/deepy/data/skills/skill-creator/SKILL.md +0 -0
  12. {deepy_cli-0.2.4 → deepy_cli-0.2.5}/src/deepy/data/skills/skill-installer/SKILL.md +0 -0
  13. {deepy_cli-0.2.4 → deepy_cli-0.2.5}/src/deepy/data/tools/AskUserQuestion.md +0 -0
  14. {deepy_cli-0.2.4 → deepy_cli-0.2.5}/src/deepy/data/tools/WebFetch.md +0 -0
  15. {deepy_cli-0.2.4 → deepy_cli-0.2.5}/src/deepy/data/tools/WebSearch.md +0 -0
  16. {deepy_cli-0.2.4 → deepy_cli-0.2.5}/src/deepy/data/tools/__init__.py +0 -0
  17. {deepy_cli-0.2.4 → deepy_cli-0.2.5}/src/deepy/data/tools/edit.md +0 -0
  18. {deepy_cli-0.2.4 → deepy_cli-0.2.5}/src/deepy/data/tools/modify.md +0 -0
  19. {deepy_cli-0.2.4 → deepy_cli-0.2.5}/src/deepy/data/tools/read.md +0 -0
  20. {deepy_cli-0.2.4 → deepy_cli-0.2.5}/src/deepy/data/tools/shell.md +0 -0
  21. {deepy_cli-0.2.4 → deepy_cli-0.2.5}/src/deepy/data/tools/todo_write.md +0 -0
  22. {deepy_cli-0.2.4 → deepy_cli-0.2.5}/src/deepy/data/tools/write.md +0 -0
  23. {deepy_cli-0.2.4 → deepy_cli-0.2.5}/src/deepy/errors.py +0 -0
  24. {deepy_cli-0.2.4 → deepy_cli-0.2.5}/src/deepy/llm/__init__.py +0 -0
  25. {deepy_cli-0.2.4 → deepy_cli-0.2.5}/src/deepy/llm/agent.py +0 -0
  26. {deepy_cli-0.2.4 → deepy_cli-0.2.5}/src/deepy/llm/compaction.py +0 -0
  27. {deepy_cli-0.2.4 → deepy_cli-0.2.5}/src/deepy/llm/context.py +0 -0
  28. {deepy_cli-0.2.4 → deepy_cli-0.2.5}/src/deepy/llm/events.py +0 -0
  29. {deepy_cli-0.2.4 → deepy_cli-0.2.5}/src/deepy/llm/model_capabilities.py +0 -0
  30. {deepy_cli-0.2.4 → deepy_cli-0.2.5}/src/deepy/llm/provider.py +0 -0
  31. {deepy_cli-0.2.4 → deepy_cli-0.2.5}/src/deepy/llm/replay.py +0 -0
  32. {deepy_cli-0.2.4 → deepy_cli-0.2.5}/src/deepy/llm/thinking.py +0 -0
  33. {deepy_cli-0.2.4 → deepy_cli-0.2.5}/src/deepy/mcp.py +0 -0
  34. {deepy_cli-0.2.4 → deepy_cli-0.2.5}/src/deepy/prompts/__init__.py +0 -0
  35. {deepy_cli-0.2.4 → deepy_cli-0.2.5}/src/deepy/prompts/compact.py +0 -0
  36. {deepy_cli-0.2.4 → deepy_cli-0.2.5}/src/deepy/prompts/init_agents.py +0 -0
  37. {deepy_cli-0.2.4 → deepy_cli-0.2.5}/src/deepy/prompts/rules.py +0 -0
  38. {deepy_cli-0.2.4 → deepy_cli-0.2.5}/src/deepy/prompts/runtime_context.py +0 -0
  39. {deepy_cli-0.2.4 → deepy_cli-0.2.5}/src/deepy/prompts/system.py +0 -0
  40. {deepy_cli-0.2.4 → deepy_cli-0.2.5}/src/deepy/prompts/tool_docs.py +0 -0
  41. {deepy_cli-0.2.4 → deepy_cli-0.2.5}/src/deepy/sessions/__init__.py +0 -0
  42. {deepy_cli-0.2.4 → deepy_cli-0.2.5}/src/deepy/sessions/jsonl.py +0 -0
  43. {deepy_cli-0.2.4 → deepy_cli-0.2.5}/src/deepy/sessions/manager.py +0 -0
  44. {deepy_cli-0.2.4 → deepy_cli-0.2.5}/src/deepy/skill_market.py +0 -0
  45. {deepy_cli-0.2.4 → deepy_cli-0.2.5}/src/deepy/skills.py +0 -0
  46. {deepy_cli-0.2.4 → deepy_cli-0.2.5}/src/deepy/status.py +0 -0
  47. {deepy_cli-0.2.4 → deepy_cli-0.2.5}/src/deepy/todos.py +0 -0
  48. {deepy_cli-0.2.4 → deepy_cli-0.2.5}/src/deepy/tools/__init__.py +0 -0
  49. {deepy_cli-0.2.4 → deepy_cli-0.2.5}/src/deepy/tools/agents.py +0 -0
  50. {deepy_cli-0.2.4 → deepy_cli-0.2.5}/src/deepy/tools/builtin.py +0 -0
  51. {deepy_cli-0.2.4 → deepy_cli-0.2.5}/src/deepy/tools/file_state.py +0 -0
  52. {deepy_cli-0.2.4 → deepy_cli-0.2.5}/src/deepy/tools/result.py +0 -0
  53. {deepy_cli-0.2.4 → deepy_cli-0.2.5}/src/deepy/tools/shell_output.py +0 -0
  54. {deepy_cli-0.2.4 → deepy_cli-0.2.5}/src/deepy/tools/shell_utils.py +0 -0
  55. {deepy_cli-0.2.4 → deepy_cli-0.2.5}/src/deepy/types/__init__.py +0 -0
  56. {deepy_cli-0.2.4 → deepy_cli-0.2.5}/src/deepy/types/sdk.py +0 -0
  57. {deepy_cli-0.2.4 → deepy_cli-0.2.5}/src/deepy/types/tool_payloads.py +0 -0
  58. {deepy_cli-0.2.4 → deepy_cli-0.2.5}/src/deepy/ui/__init__.py +0 -0
  59. {deepy_cli-0.2.4 → deepy_cli-0.2.5}/src/deepy/ui/app.py +0 -0
  60. {deepy_cli-0.2.4 → deepy_cli-0.2.5}/src/deepy/ui/ask_user_question.py +0 -0
  61. {deepy_cli-0.2.4 → deepy_cli-0.2.5}/src/deepy/ui/exit_summary.py +0 -0
  62. {deepy_cli-0.2.4 → deepy_cli-0.2.5}/src/deepy/ui/file_mentions.py +0 -0
  63. {deepy_cli-0.2.4 → deepy_cli-0.2.5}/src/deepy/ui/loading_text.py +0 -0
  64. {deepy_cli-0.2.4 → deepy_cli-0.2.5}/src/deepy/ui/local_command.py +0 -0
  65. {deepy_cli-0.2.4 → deepy_cli-0.2.5}/src/deepy/ui/markdown.py +0 -0
  66. {deepy_cli-0.2.4 → deepy_cli-0.2.5}/src/deepy/ui/message_view.py +0 -0
  67. {deepy_cli-0.2.4 → deepy_cli-0.2.5}/src/deepy/ui/model_picker.py +0 -0
  68. {deepy_cli-0.2.4 → deepy_cli-0.2.5}/src/deepy/ui/prompt_buffer.py +0 -0
  69. {deepy_cli-0.2.4 → deepy_cli-0.2.5}/src/deepy/ui/prompt_input.py +0 -0
  70. {deepy_cli-0.2.4 → deepy_cli-0.2.5}/src/deepy/ui/session_list.py +0 -0
  71. {deepy_cli-0.2.4 → deepy_cli-0.2.5}/src/deepy/ui/session_picker.py +0 -0
  72. {deepy_cli-0.2.4 → deepy_cli-0.2.5}/src/deepy/ui/skill_picker.py +0 -0
  73. {deepy_cli-0.2.4 → deepy_cli-0.2.5}/src/deepy/ui/slash_commands.py +0 -0
  74. {deepy_cli-0.2.4 → deepy_cli-0.2.5}/src/deepy/ui/status_footer.py +0 -0
  75. {deepy_cli-0.2.4 → deepy_cli-0.2.5}/src/deepy/ui/styles.py +0 -0
  76. {deepy_cli-0.2.4 → deepy_cli-0.2.5}/src/deepy/ui/terminal.py +0 -0
  77. {deepy_cli-0.2.4 → deepy_cli-0.2.5}/src/deepy/ui/theme_picker.py +0 -0
  78. {deepy_cli-0.2.4 → deepy_cli-0.2.5}/src/deepy/ui/thinking_state.py +0 -0
  79. {deepy_cli-0.2.4 → deepy_cli-0.2.5}/src/deepy/ui/welcome.py +0 -0
  80. {deepy_cli-0.2.4 → deepy_cli-0.2.5}/src/deepy/update_check.py +0 -0
  81. {deepy_cli-0.2.4 → deepy_cli-0.2.5}/src/deepy/usage.py +0 -0
  82. {deepy_cli-0.2.4 → deepy_cli-0.2.5}/src/deepy/utils/__init__.py +0 -0
  83. {deepy_cli-0.2.4 → deepy_cli-0.2.5}/src/deepy/utils/debug_logger.py +0 -0
  84. {deepy_cli-0.2.4 → deepy_cli-0.2.5}/src/deepy/utils/error_logger.py +0 -0
  85. {deepy_cli-0.2.4 → deepy_cli-0.2.5}/src/deepy/utils/json.py +0 -0
  86. {deepy_cli-0.2.4 → deepy_cli-0.2.5}/src/deepy/utils/notify.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.3
2
2
  Name: deepy-cli
3
- Version: 0.2.4
3
+ Version: 0.2.5
4
4
  Summary: Deepy - Vibe coding for DeepSeek models in your terminal
5
5
  Keywords: deepseek,coding-agent,terminal,cli,agents
6
6
  Author: kirineko
@@ -1,6 +1,6 @@
1
1
  [project]
2
2
  name = "deepy-cli"
3
- version = "0.2.4"
3
+ version = "0.2.5"
4
4
  description = "Deepy - Vibe coding for DeepSeek models in your terminal"
5
5
  readme = "README.md"
6
6
  authors = [
@@ -1,6 +1,6 @@
1
1
  from __future__ import annotations
2
2
 
3
- __version__ = "0.2.4"
3
+ __version__ = "0.2.5"
4
4
 
5
5
 
6
6
  def main() -> None:
@@ -25,6 +25,11 @@ from .events import DeepyStreamEvent, normalize_stream_event
25
25
  from .provider import ProviderBundle, build_provider_bundle
26
26
 
27
27
  DEFAULT_MAX_TURNS = 100
28
+ INTERRUPTED_MARKER_TEXT = (
29
+ "Interrupted by user with Esc. This turn was stopped before completion. "
30
+ "Do not continue the interrupted request unless the user explicitly asks to continue."
31
+ )
32
+ INTERRUPTED_TOOL_OUTPUT_TEXT = "Tool execution interrupted by user with Esc."
28
33
 
29
34
 
30
35
  @dataclass(frozen=True)
@@ -128,6 +133,7 @@ async def run_prompt_once(
128
133
  pending_questions: list[dict[str, Any]] = []
129
134
  usage = TokenUsage()
130
135
  interrupt_task: asyncio.Task[bool] | None = None
136
+ session_baseline_count = len(await session.get_items())
131
137
  try:
132
138
  result = Runner.run_streamed(
133
139
  agent,
@@ -272,6 +278,12 @@ async def run_prompt_once(
272
278
  raise
273
279
 
274
280
  interrupted = interrupted or await _finish_interrupt_task(interrupt_task)
281
+ if interrupted:
282
+ await _reconcile_interrupted_session_tail(
283
+ session,
284
+ baseline_count=session_baseline_count,
285
+ prompt=prompt,
286
+ )
275
287
 
276
288
  final_output = getattr(result, "final_output", None)
277
289
  output = final_output if isinstance(final_output, str) else "".join(chunks)
@@ -512,6 +524,143 @@ async def _finish_interrupt_task(task: asyncio.Task[bool] | None) -> bool:
512
524
  return False
513
525
 
514
526
 
527
+ async def _reconcile_interrupted_session_tail(
528
+ session: DeepyJsonlSession,
529
+ *,
530
+ baseline_count: int,
531
+ prompt: str,
532
+ ) -> None:
533
+ items = await session.get_items()
534
+ if baseline_count < 0 or baseline_count > len(items):
535
+ return
536
+ suffix = items[baseline_count:]
537
+ if not suffix:
538
+ return
539
+
540
+ if len(suffix) == 1 and _is_user_prompt_item(suffix[0], prompt):
541
+ await session.pop_item()
542
+ return
543
+
544
+ additions = _interrupted_tool_output_items(suffix)
545
+ if not _is_interrupt_marker_item(suffix[-1]):
546
+ additions.append(_interrupted_marker_item())
547
+ if additions:
548
+ await session.add_items(additions)
549
+
550
+
551
+ def _is_user_prompt_item(item: dict[str, Any], prompt: str) -> bool:
552
+ if item.get("role") != "user":
553
+ return False
554
+ return _item_text_content(item) == prompt
555
+
556
+
557
+ def _item_text_content(item: dict[str, Any]) -> str:
558
+ content = item.get("content")
559
+ if isinstance(content, str):
560
+ return content
561
+ if not isinstance(content, list):
562
+ return ""
563
+ parts: list[str] = []
564
+ for part in content:
565
+ if isinstance(part, dict):
566
+ text = part.get("text")
567
+ if text is None:
568
+ text = part.get("input_text")
569
+ if isinstance(text, str):
570
+ parts.append(text)
571
+ return "".join(parts)
572
+
573
+
574
+ def _interrupted_tool_output_items(items: list[dict[str, Any]]) -> list[dict[str, Any]]:
575
+ output_call_ids = {
576
+ call_id
577
+ for item in items
578
+ if (call_id := _function_call_output_id(item))
579
+ }
580
+ additions: list[dict[str, Any]] = []
581
+ added_call_ids: set[str] = set()
582
+ for item in items:
583
+ for call_id, output_item in _missing_output_items_for_call(item, output_call_ids):
584
+ if call_id in added_call_ids:
585
+ continue
586
+ additions.append(output_item)
587
+ added_call_ids.add(call_id)
588
+ return additions
589
+
590
+
591
+ def _missing_output_items_for_call(
592
+ item: dict[str, Any],
593
+ output_call_ids: set[str],
594
+ ) -> list[tuple[str, dict[str, Any]]]:
595
+ call_id = _function_call_id(item)
596
+ if call_id:
597
+ return (
598
+ []
599
+ if call_id in output_call_ids
600
+ else [
601
+ (
602
+ call_id,
603
+ {
604
+ "type": "function_call_output",
605
+ "call_id": call_id,
606
+ "output": INTERRUPTED_TOOL_OUTPUT_TEXT,
607
+ },
608
+ )
609
+ ]
610
+ )
611
+
612
+ missing: list[tuple[str, dict[str, Any]]] = []
613
+ if item.get("role") != "assistant":
614
+ return missing
615
+ tool_calls = item.get("tool_calls")
616
+ if not isinstance(tool_calls, list):
617
+ return missing
618
+ for tool_call in tool_calls:
619
+ if not isinstance(tool_call, dict):
620
+ continue
621
+ chat_call_id = tool_call.get("id")
622
+ if not isinstance(chat_call_id, str) or not chat_call_id or chat_call_id in output_call_ids:
623
+ continue
624
+ missing.append(
625
+ (
626
+ chat_call_id,
627
+ {
628
+ "role": "tool",
629
+ "tool_call_id": chat_call_id,
630
+ "content": INTERRUPTED_TOOL_OUTPUT_TEXT,
631
+ },
632
+ )
633
+ )
634
+ return missing
635
+
636
+
637
+ def _function_call_id(item: dict[str, Any]) -> str:
638
+ if item.get("type") != "function_call":
639
+ return ""
640
+ call_id = item.get("call_id")
641
+ if call_id is None:
642
+ call_id = item.get("id")
643
+ return call_id if isinstance(call_id, str) else ""
644
+
645
+
646
+ def _function_call_output_id(item: dict[str, Any]) -> str:
647
+ if item.get("type") == "function_call_output":
648
+ call_id = item.get("call_id")
649
+ return call_id if isinstance(call_id, str) else ""
650
+ if item.get("role") == "tool":
651
+ tool_call_id = item.get("tool_call_id")
652
+ return tool_call_id if isinstance(tool_call_id, str) else ""
653
+ return ""
654
+
655
+
656
+ def _interrupted_marker_item() -> dict[str, Any]:
657
+ return {"role": "assistant", "content": INTERRUPTED_MARKER_TEXT}
658
+
659
+
660
+ def _is_interrupt_marker_item(item: dict[str, Any]) -> bool:
661
+ return item.get("role") == "assistant" and item.get("content") == INTERRUPTED_MARKER_TEXT
662
+
663
+
515
664
  def _pending_questions_from_tool_output(output: str) -> list[dict[str, Any]]:
516
665
  if not output.strip():
517
666
  return []
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes