@scemoon/cdh 1.0.4 → 1.0.6
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/ai-dlc-skill/SKILL.md +4 -2
- package/ai-dlc-skill/agents/master.md +6 -0
- package/ai-dlc-skill/agents/plan-agent.md +2 -2
- package/ai-dlc-skill/agents/understand-agent.md +4 -4
- package/ai-dlc-skill/agents/verify-agent.md +1 -1
- package/ai-dlc-skill/architecture/project-structure.md +10 -10
- package/ai-dlc-skill/brownfield/README.md +2 -2
- package/ai-dlc-skill/brownfield/generate-context.sh +3 -3
- package/ai-dlc-skill/brownfield/scripts/extract-api.sh +5 -5
- package/ai-dlc-skill/components/backend.md +1 -1
- package/ai-dlc-skill/components/web.md +2 -2
- package/ai-dlc-skill/contracts/README.md +3 -3
- package/ai-dlc-skill/cross-tool/cline/export.sh +1 -1
- package/ai-dlc-skill/cross-tool/copilot/export.sh +1 -1
- package/ai-dlc-skill/cross-tool/onecode/export-skills.sh +7 -3
- package/ai-dlc-skill/cross-tool/onecode/export.sh +14 -8
- package/ai-dlc-skill/phases/deliver/lifecycle.md +6 -6
- package/ai-dlc-skill/phases/deliver/prompt.md +2 -2
- package/ai-dlc-skill/phases/deliver/rules.md +1 -1
- package/ai-dlc-skill/phases/plan/lifecycle.md +11 -11
- package/ai-dlc-skill/phases/plan/prompt.md +6 -6
- package/ai-dlc-skill/phases/plan/rules.md +1 -1
- package/ai-dlc-skill/phases/understand/entry.md +1 -1
- package/ai-dlc-skill/phases/understand/lifecycle.md +7 -7
- package/ai-dlc-skill/phases/understand/prompt.md +8 -8
- package/ai-dlc-skill/phases/understand/rules.md +5 -5
- package/ai-dlc-skill/phases/verify/lifecycle.md +24 -24
- package/ai-dlc-skill/phases/verify/prompt.md +8 -8
- package/ai-dlc-skill/phases/verify/rules.md +9 -9
- package/ai-dlc-skill/providers/README.md +2 -2
- package/ai-dlc-skill/providers/aliyun/preview.yaml +2 -2
- package/ai-dlc-skill/providers/tcb/preview.yaml +2 -2
- package/ai-dlc-skill/requirements.md +7 -7
- package/ai-dlc-skill/skill.yaml +4 -4
- package/ai-dlc-skill/templates/integration/CHANGELOG.md +2 -2
- package/ai-dlc-skill/templates/integration/contract-diff.md +7 -7
- package/ai-dlc-skill/templates/integration/contract-spec.md +7 -7
- package/ai-dlc-skill/templates/integration/runtime-contract.yaml +1 -1
- package/ai-dlc-skill/templates/project/README.md +18 -19
- package/ai-dlc-skill/templates/project/template.md +21 -21
- package/ai-dlc-skill/walkthrough/collect.sh +1 -1
- package/ai-dlc-skill/walkthrough/template.md +2 -2
- package/ai-dlc-skill/workflows/ai-dlc.yaml +12 -12
- package/ai-dlc-skill/workflows/deployment.yaml +4 -4
- package/cdh/__pycache__/cdh_mcp_injector.cpython-314.pyc +0 -0
- package/cdh/__pycache__/cdh_mcp_loader.cpython-314.pyc +0 -0
- package/cdh/__pycache__/cdh_mcp_manager.cpython-314.pyc +0 -0
- package/cdh/__pycache__/cdh_session_aggregator.cpython-314.pyc +0 -0
- package/cdh/__pycache__/cdh_skill_loader.cpython-314.pyc +0 -0
- package/cdh/__pycache__/cdh_skill_manager.cpython-314.pyc +0 -0
- package/cdh/__pycache__/cli.cpython-313.pyc +0 -0
- package/cdh/__pycache__/cli.cpython-314.pyc +0 -0
- package/cdh/__pycache__/scaffold.cpython-314.pyc +0 -0
- package/cdh/cdh_mcp_injector.py +115 -0
- package/cdh/cdh_mcp_loader.py +67 -0
- package/cdh/cdh_mcp_manager.py +73 -0
- package/cdh/cdh_session_aggregator.py +258 -0
- package/cdh/cdh_skill_manager.py +105 -0
- package/cdh/cli.py +64 -4
- package/cdh/scaffold.py +192 -41
- package/onecode/__init__.py +1 -1
- package/onecode/__pycache__/cli.cpython-314.pyc +0 -0
- package/onecode/__pycache__/commands.cpython-314.pyc +0 -0
- package/onecode/__pycache__/config.cpython-314.pyc +0 -0
- package/onecode/__pycache__/config_screen.cpython-314.pyc +0 -0
- package/onecode/__pycache__/migrate.cpython-314.pyc +0 -0
- package/onecode/agent/__pycache__/cdh_loader.cpython-314.pyc +0 -0
- package/onecode/agent/__pycache__/context.cpython-314.pyc +0 -0
- package/onecode/agent/__pycache__/engine.cpython-314.pyc +0 -0
- package/onecode/agent/__pycache__/hooks.cpython-314.pyc +0 -0
- package/onecode/agent/__pycache__/onecode_agent_acp.cpython-314.pyc +0 -0
- package/onecode/agent/__pycache__/permissions.cpython-314.pyc +0 -0
- package/onecode/agent/__pycache__/project_doc.cpython-314.pyc +0 -0
- package/onecode/agent/__pycache__/session.cpython-314.pyc +0 -0
- package/onecode/agent/agents/__pycache__/types.cpython-314.pyc +0 -0
- package/onecode/agent/agents/types.py +47 -164
- package/onecode/agent/attachments.py +0 -1
- package/onecode/agent/cdh_loader.py +17 -25
- package/onecode/agent/context.py +8 -3
- package/onecode/agent/engine.py +835 -220
- package/onecode/agent/hooks.py +1 -1
- package/onecode/agent/onecode_agent_acp.py +460 -47
- package/onecode/agent/onecode_agent_acp.py.bak +2447 -0
- package/onecode/agent/permissions.py +0 -1
- package/onecode/agent/project_doc.py +19 -0
- package/onecode/agent/session.py +17 -3
- package/onecode/agent/snapshot.py +3 -4
- package/onecode/agent/tools/__pycache__/agent_tools.cpython-314.pyc +0 -0
- package/onecode/agent/tools/__pycache__/apply_patch_tool.cpython-314.pyc +0 -0
- package/onecode/agent/tools/__pycache__/bash_tool.cpython-314.pyc +0 -0
- package/onecode/agent/tools/__pycache__/communication_tools.cpython-314.pyc +0 -0
- package/onecode/agent/tools/__pycache__/config_tool.cpython-314.pyc +0 -0
- package/onecode/agent/tools/__pycache__/cron_tools.cpython-314.pyc +0 -0
- package/onecode/agent/tools/__pycache__/file_ops.cpython-314.pyc +0 -0
- package/onecode/agent/tools/__pycache__/file_tools.cpython-314.pyc +0 -0
- package/onecode/agent/tools/__pycache__/git_tools.cpython-314.pyc +0 -0
- package/onecode/agent/tools/__pycache__/lsp_tools.cpython-314.pyc +0 -0
- package/onecode/agent/tools/__pycache__/mcp_tools.cpython-314.pyc +0 -0
- package/onecode/agent/tools/__pycache__/permission_handler.cpython-314.pyc +0 -0
- package/onecode/agent/tools/__pycache__/protocol.cpython-314.pyc +0 -0
- package/onecode/agent/tools/__pycache__/registry.cpython-314.pyc +0 -0
- package/onecode/agent/tools/__pycache__/sandbox.cpython-314.pyc +0 -0
- package/onecode/agent/tools/__pycache__/skill_tools.cpython-314.pyc +0 -0
- package/onecode/agent/tools/__pycache__/todo_tools.cpython-314.pyc +0 -0
- package/onecode/agent/tools/__pycache__/web_tools.cpython-314.pyc +0 -0
- package/onecode/agent/tools/agent_tools.py +4 -6
- package/onecode/agent/tools/apply_patch_tool.py +2 -8
- package/onecode/agent/tools/bash_tool.py +32 -12
- package/onecode/agent/tools/communication_tools.py +4 -5
- package/onecode/agent/tools/config_tool.py +3 -3
- package/onecode/agent/tools/cron_tools.py +4 -5
- package/onecode/agent/tools/file_ops.py +37 -3
- package/onecode/agent/tools/file_tools.py +10 -11
- package/onecode/agent/tools/git_tools.py +2 -2
- package/onecode/agent/tools/lsp_tools.py +2 -2
- package/onecode/agent/tools/mcp_tools.py +3 -5
- package/onecode/agent/tools/permission_handler.py +1 -1
- package/onecode/agent/tools/protocol.py +1 -1
- package/onecode/agent/tools/registry.py +69 -2
- package/onecode/agent/tools/sandbox.py +110 -16
- package/onecode/agent/tools/skill_tools.py +2 -2
- package/onecode/agent/tools/todo_tools.py +38 -19
- package/onecode/agent/tools/web_tools.py +6 -7
- package/onecode/builtin_skills/agent-browser/SKILL.md +212 -0
- package/onecode/builtin_skills/agent-browser/skill.yaml +8 -0
- package/onecode/builtin_skills/skill-creator/SKILL.md +106 -0
- package/onecode/builtin_skills/skill-creator/skill.yaml +8 -0
- package/onecode/cli.py +186 -6
- package/onecode/codebase/__pycache__/chunker.cpython-314.pyc +0 -0
- package/onecode/codebase/__pycache__/indexer.cpython-314.pyc +0 -0
- package/onecode/codebase/__pycache__/retriever.cpython-314.pyc +0 -0
- package/onecode/codebase/__pycache__/storage.cpython-314.pyc +0 -0
- package/onecode/codebase/chunker.py +0 -5
- package/onecode/codebase/indexer.py +14 -1
- package/onecode/codebase/retriever.py +13 -2
- package/onecode/codebase/storage.py +5 -4
- package/onecode/commands.py +81 -6
- package/onecode/config.py +41 -12
- package/onecode/config_screen.py +7 -3
- package/onecode/mcp/__pycache__/manager.cpython-314.pyc +0 -0
- package/onecode/mcp/manager.py +2 -3
- package/onecode/memory/__init__.py +7 -53
- package/onecode/memory/__pycache__/__init__.cpython-314.pyc +0 -0
- package/onecode/memory/__pycache__/backend.cpython-314.pyc +0 -0
- package/onecode/memory/__pycache__/offload.cpython-314.pyc +0 -0
- package/onecode/memory/__pycache__/pyramid.cpython-314.pyc +0 -0
- package/onecode/memory/__pycache__/recall.cpython-314.pyc +0 -0
- package/onecode/memory/backend.py +13 -39
- package/onecode/memory/pyramid.py +2 -71
- package/onecode/memory/recall.py +38 -3
- package/onecode/migrate.py +122 -0
- package/onecode/models/__pycache__/messages.cpython-314.pyc +0 -0
- package/onecode/models/__pycache__/provider.cpython-314.pyc +0 -0
- package/onecode/models/messages.py +58 -0
- package/onecode/models/provider.py +20 -2
- package/onecode/models/providers/__pycache__/anthropic_provider.cpython-314.pyc +0 -0
- package/onecode/models/providers/__pycache__/glm_provider.cpython-314.pyc +0 -0
- package/onecode/models/providers/__pycache__/minimax_provider.cpython-314.pyc +0 -0
- package/onecode/models/providers/__pycache__/minimaxi_provider.cpython-314.pyc +0 -0
- package/onecode/models/providers/__pycache__/ollama_provider.cpython-314.pyc +0 -0
- package/onecode/models/providers/anthropic_provider.py +59 -12
- package/onecode/models/providers/glm_provider.py +40 -0
- package/onecode/models/providers/minimax_provider.py +3 -0
- package/onecode/models/providers/minimaxi_provider.py +3 -0
- package/onecode/models/providers/ollama_provider.py +3 -0
- package/onecode/server/app.py +1 -1
- package/onecode/skills/__pycache__/argument_substitution.cpython-314.pyc +0 -0
- package/onecode/skills/__pycache__/bootstrap.cpython-314.pyc +0 -0
- package/onecode/skills/__pycache__/loader.cpython-314.pyc +0 -0
- package/onecode/skills/__pycache__/manager.cpython-314.pyc +0 -0
- package/onecode/skills/argument_substitution.py +0 -1
- package/onecode/skills/bootstrap.py +124 -0
- package/onecode/skills/loader.py +35 -53
- package/onecode/skills/manager.py +2 -2
- package/onecode/storage/activity.py +3 -4
- package/onecode/storage/session.py +3 -4
- package/onecode/tasks/manager.py +23 -10
- package/onecode/trace/tracer.py +2 -3
- package/package.json +1 -1
- package/pyproject.toml +5 -1
- package/run.js +68 -14
- package/tui/__pycache__/about.cpython-314.pyc +0 -0
- package/tui/__pycache__/app.cpython-313.pyc +0 -0
- package/tui/__pycache__/app.cpython-314.pyc +0 -0
- package/tui/__pycache__/danger.cpython-314.pyc +0 -0
- package/tui/__pycache__/directory_suggester.cpython-314.pyc +0 -0
- package/tui/__pycache__/messages.cpython-314.pyc +0 -0
- package/tui/__pycache__/path_complete.cpython-314.pyc +0 -0
- package/tui/__pycache__/paths.cpython-314.pyc +0 -0
- package/tui/__pycache__/session_tracker.cpython-314.pyc +0 -0
- package/tui/__pycache__/settings_schema.cpython-314.pyc +0 -0
- package/tui/acp/__pycache__/agent.cpython-314.pyc +0 -0
- package/tui/acp/__pycache__/api.cpython-314.pyc +0 -0
- package/tui/acp/__pycache__/messages.cpython-314.pyc +0 -0
- package/tui/acp/__pycache__/prompt.cpython-314.pyc +0 -0
- package/tui/acp/__pycache__/protocol.cpython-314.pyc +0 -0
- package/tui/acp/agent.py +125 -58
- package/tui/acp/api.py +12 -0
- package/tui/acp/messages.py +3 -1
- package/tui/acp/prompt.py +0 -57
- package/tui/acp/protocol.py +6 -0
- package/tui/app.py +11 -33
- package/tui/messages.py +2 -0
- package/tui/path_complete.py +1 -1
- package/tui/paths.py +16 -0
- package/tui/screens/__pycache__/agent_modal.cpython-314.pyc +0 -0
- package/tui/screens/__pycache__/component_picker.cpython-314.pyc +0 -0
- package/tui/screens/__pycache__/diff_screen.cpython-314.pyc +0 -0
- package/tui/screens/__pycache__/log.cpython-314.pyc +0 -0
- package/tui/screens/__pycache__/main.cpython-313.pyc +0 -0
- package/tui/screens/__pycache__/main.cpython-314.pyc +0 -0
- package/tui/screens/__pycache__/permissions.cpython-314.pyc +0 -0
- package/tui/screens/__pycache__/projects_app.cpython-314.pyc +0 -0
- package/tui/screens/__pycache__/projects_screen.cpython-314.pyc +0 -0
- package/tui/screens/__pycache__/sessions.cpython-314.pyc +0 -0
- package/tui/screens/__pycache__/store.cpython-314.pyc +0 -0
- package/tui/screens/__pycache__/tool_content_screen.cpython-314.pyc +0 -0
- package/tui/screens/component_picker.py +35 -15
- package/tui/screens/diff_screen.py +84 -0
- package/tui/screens/log.py +25 -25
- package/tui/screens/main.py +48 -26
- package/tui/screens/projects.tcss +5 -1
- package/tui/screens/projects_app.py +6 -6
- package/tui/screens/projects_screen.py +19 -39
- package/tui/screens/sessions.py +3 -5
- package/tui/session_tracker.py +3 -0
- package/tui/settings_schema.py +1 -1
- package/tui/visuals/__pycache__/columns.cpython-314.pyc +0 -0
- package/tui/visuals/columns.py +1 -2
- package/tui/widgets/__pycache__/acp_content.cpython-314.pyc +0 -0
- package/tui/widgets/__pycache__/agent_response.cpython-314.pyc +0 -0
- package/tui/widgets/__pycache__/agent_thought.cpython-314.pyc +0 -0
- package/tui/widgets/__pycache__/conversation.cpython-314.pyc +0 -0
- package/tui/widgets/__pycache__/diff_view.cpython-314.pyc +0 -0
- package/tui/widgets/__pycache__/directory_input.cpython-314.pyc +0 -0
- package/tui/widgets/__pycache__/future_text.cpython-314.pyc +0 -0
- package/tui/widgets/__pycache__/grid_select.cpython-314.pyc +0 -0
- package/tui/widgets/__pycache__/load_more.cpython-314.pyc +0 -0
- package/tui/widgets/__pycache__/mandelbrot.cpython-314.pyc +0 -0
- package/tui/widgets/__pycache__/markdown_note.cpython-314.pyc +0 -0
- package/tui/widgets/__pycache__/modified_files.cpython-314.pyc +0 -0
- package/tui/widgets/__pycache__/plan.cpython-314.pyc +0 -0
- package/tui/widgets/__pycache__/project_grid_select.cpython-314.pyc +0 -0
- package/tui/widgets/__pycache__/project_summary.cpython-314.pyc +0 -0
- package/tui/widgets/__pycache__/session_grid_select.cpython-314.pyc +0 -0
- package/tui/widgets/__pycache__/session_summary.cpython-314.pyc +0 -0
- package/tui/widgets/__pycache__/shell_result.cpython-314.pyc +0 -0
- package/tui/widgets/__pycache__/side_bar.cpython-314.pyc +0 -0
- package/tui/widgets/__pycache__/subagent.cpython-314.pyc +0 -0
- package/tui/widgets/__pycache__/subagent_screen.cpython-314.pyc +0 -0
- package/tui/widgets/__pycache__/terminal.cpython-314.pyc +0 -0
- package/tui/widgets/__pycache__/tool_call.cpython-314.pyc +0 -0
- package/tui/widgets/agent_response.py +0 -1
- package/tui/widgets/agent_thought.py +0 -1
- package/tui/widgets/conversation.py +157 -14
- package/tui/widgets/diff_view.py +10 -0
- package/tui/widgets/load_more.py +24 -0
- package/tui/widgets/modified_files.py +86 -12
- package/tui/widgets/plan.py +1 -1
- package/tui/widgets/project_grid_select.py +9 -2
- package/tui/widgets/session_grid_select.py +6 -0
- package/tui/widgets/side_bar.py +10 -0
- package/tui/widgets/subagent.py +92 -58
- package/tui/widgets/subagent_screen.py +638 -115
- package/tui/widgets/terminal.py +1 -2
- package/tui/widgets/terminal_tool.py +11 -2
- package/tui/widgets/tool_call.py +35 -2
- package/onecode/memory/offload.py +0 -114
- package/onecode/memory/symbolic.py +0 -164
|
@@ -1,15 +1,14 @@
|
|
|
1
1
|
from __future__ import annotations
|
|
2
2
|
|
|
3
|
-
import json
|
|
4
3
|
import uuid
|
|
5
4
|
from datetime import datetime, timezone
|
|
6
5
|
from pathlib import Path
|
|
7
6
|
from typing import Optional
|
|
8
7
|
|
|
9
|
-
from sqlalchemy import Column, String,
|
|
8
|
+
from sqlalchemy import Column, String, DateTime, JSON, create_engine
|
|
10
9
|
from sqlalchemy.orm import declarative_base, sessionmaker
|
|
11
10
|
|
|
12
|
-
from onecode.config import
|
|
11
|
+
from onecode.config import ONECODE_DIR
|
|
13
12
|
|
|
14
13
|
Base = declarative_base()
|
|
15
14
|
|
|
@@ -28,7 +27,7 @@ class ActivityRecord(Base):
|
|
|
28
27
|
class ActivityRecorder:
|
|
29
28
|
def __init__(self, db_path: Optional[Path] = None):
|
|
30
29
|
if db_path is None:
|
|
31
|
-
db_path =
|
|
30
|
+
db_path = ONECODE_DIR / "sessions" / "cdh.db"
|
|
32
31
|
db_path.parent.mkdir(parents=True, exist_ok=True)
|
|
33
32
|
self.engine = create_engine(f"sqlite:///{db_path}")
|
|
34
33
|
Base.metadata.create_all(self.engine)
|
|
@@ -9,14 +9,13 @@ from typing import Optional
|
|
|
9
9
|
from sqlalchemy import (
|
|
10
10
|
Column,
|
|
11
11
|
String,
|
|
12
|
-
Text,
|
|
13
12
|
DateTime,
|
|
14
13
|
JSON,
|
|
15
14
|
create_engine,
|
|
16
15
|
)
|
|
17
|
-
from sqlalchemy.orm import declarative_base,
|
|
16
|
+
from sqlalchemy.orm import declarative_base, sessionmaker
|
|
18
17
|
|
|
19
|
-
from onecode.config import
|
|
18
|
+
from onecode.config import ONECODE_DIR
|
|
20
19
|
|
|
21
20
|
Base = declarative_base()
|
|
22
21
|
|
|
@@ -43,7 +42,7 @@ class SessionRecord(Base):
|
|
|
43
42
|
class SessionStore:
|
|
44
43
|
def __init__(self, db_path: Optional[Path] = None):
|
|
45
44
|
if db_path is None:
|
|
46
|
-
db_path =
|
|
45
|
+
db_path = ONECODE_DIR / "sessions" / "cdh.db"
|
|
47
46
|
db_path.parent.mkdir(parents=True, exist_ok=True)
|
|
48
47
|
self.engine = create_engine(f"sqlite:///{db_path}")
|
|
49
48
|
Base.metadata.create_all(self.engine)
|
package/onecode/tasks/manager.py
CHANGED
|
@@ -3,7 +3,7 @@ from __future__ import annotations
|
|
|
3
3
|
import asyncio
|
|
4
4
|
import logging
|
|
5
5
|
import uuid
|
|
6
|
-
from dataclasses import dataclass
|
|
6
|
+
from dataclasses import dataclass
|
|
7
7
|
from datetime import datetime
|
|
8
8
|
from pathlib import Path
|
|
9
9
|
from typing import Any, Callable, Optional
|
|
@@ -16,8 +16,6 @@ from onecode.tasks.models import (
|
|
|
16
16
|
TaskChecklistState,
|
|
17
17
|
TaskGateRecord,
|
|
18
18
|
TaskToolCallSummary,
|
|
19
|
-
TodoItem,
|
|
20
|
-
TodoStatus,
|
|
21
19
|
)
|
|
22
20
|
|
|
23
21
|
|
|
@@ -56,6 +54,8 @@ class TaskManager:
|
|
|
56
54
|
self._storage_path = storage_path or Path.home() / ".cdh" / "tasks"
|
|
57
55
|
self._executor: Optional[TaskExecutor] = None
|
|
58
56
|
self._worker_task: Optional[asyncio.Task] = None
|
|
57
|
+
self._semaphore = asyncio.Semaphore(5)
|
|
58
|
+
self._shutdown: bool = False
|
|
59
59
|
|
|
60
60
|
def set_executor(self, executor: TaskExecutor) -> None:
|
|
61
61
|
self._executor = executor
|
|
@@ -90,11 +90,14 @@ class TaskManager:
|
|
|
90
90
|
self._worker_task = asyncio.create_task(self._worker_loop())
|
|
91
91
|
|
|
92
92
|
async def _worker_loop(self) -> None:
|
|
93
|
-
while
|
|
93
|
+
while not self._shutdown:
|
|
94
94
|
try:
|
|
95
|
-
task = await self._queue.get()
|
|
95
|
+
task = await asyncio.wait_for(self._queue.get(), timeout=30)
|
|
96
96
|
if task.id not in self._running:
|
|
97
|
-
|
|
97
|
+
async with self._semaphore:
|
|
98
|
+
self._running[task.id] = asyncio.create_task(self._run_task(task))
|
|
99
|
+
except asyncio.TimeoutError:
|
|
100
|
+
continue
|
|
98
101
|
except asyncio.CancelledError:
|
|
99
102
|
break
|
|
100
103
|
except Exception as e:
|
|
@@ -112,7 +115,10 @@ class TaskManager:
|
|
|
112
115
|
if self._executor:
|
|
113
116
|
cancel = asyncio.Event()
|
|
114
117
|
events: asyncio.Queue = asyncio.Queue()
|
|
115
|
-
result = await
|
|
118
|
+
result = await asyncio.wait_for(
|
|
119
|
+
self._executor.execute(execution, events, cancel),
|
|
120
|
+
timeout=300,
|
|
121
|
+
)
|
|
116
122
|
task_record.status = TaskStatus.COMPLETED if result.get("success") else TaskStatus.FAILED
|
|
117
123
|
task_record.error = result.get("error")
|
|
118
124
|
else:
|
|
@@ -147,8 +153,7 @@ class TaskManager:
|
|
|
147
153
|
return list(self._tasks.values())
|
|
148
154
|
return [t for t in self._tasks.values() if t.status == status]
|
|
149
155
|
|
|
150
|
-
def add_todo_to_task(self, task_id: str, content: str, status: str = "pending") -> Optional["TaskChecklistItem"]:
|
|
151
|
-
from onecode.tasks.models import TaskChecklistItem
|
|
156
|
+
def add_todo_to_task(self, task_id: str, content: str, status: str = "pending") -> Optional["TaskChecklistItem"]: # noqa: F821
|
|
152
157
|
task = self._tasks.get(task_id)
|
|
153
158
|
if not task:
|
|
154
159
|
return None
|
|
@@ -211,9 +216,17 @@ class TaskManager:
|
|
|
211
216
|
return True
|
|
212
217
|
|
|
213
218
|
async def stop(self) -> None:
|
|
219
|
+
self._shutdown = True
|
|
214
220
|
if self._worker_task:
|
|
215
221
|
self._worker_task.cancel()
|
|
216
222
|
try:
|
|
217
223
|
await self._worker_task
|
|
218
224
|
except asyncio.CancelledError:
|
|
219
|
-
pass
|
|
225
|
+
pass
|
|
226
|
+
if self._running:
|
|
227
|
+
results = await asyncio.gather(
|
|
228
|
+
*self._running.values(), return_exceptions=True,
|
|
229
|
+
)
|
|
230
|
+
for tid, res in zip(list(self._running.keys()), results):
|
|
231
|
+
if isinstance(res, Exception):
|
|
232
|
+
logger.warning("Task %s exited with error: %s", tid, res)
|
package/onecode/trace/tracer.py
CHANGED
|
@@ -5,10 +5,9 @@ import time
|
|
|
5
5
|
import uuid
|
|
6
6
|
from contextlib import contextmanager
|
|
7
7
|
from datetime import datetime, timezone
|
|
8
|
-
from pathlib import Path
|
|
9
8
|
from typing import Any, Optional
|
|
10
9
|
|
|
11
|
-
from onecode.config import
|
|
10
|
+
from onecode.config import ONECODE_DIR
|
|
12
11
|
|
|
13
12
|
|
|
14
13
|
class Span:
|
|
@@ -55,7 +54,7 @@ class Tracer:
|
|
|
55
54
|
self.recording: bool = False
|
|
56
55
|
self.spans: list[Span] = []
|
|
57
56
|
self.stack: list[str] = []
|
|
58
|
-
self.trace_dir =
|
|
57
|
+
self.trace_dir = ONECODE_DIR / "traces"
|
|
59
58
|
self.trace_dir.mkdir(parents=True, exist_ok=True)
|
|
60
59
|
self._otlp_endpoint: Optional[str] = None
|
|
61
60
|
|
package/package.json
CHANGED
package/pyproject.toml
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
[project]
|
|
2
2
|
name = "cloud-dev-harness"
|
|
3
|
-
version = "1.0.
|
|
3
|
+
version = "1.0.5"
|
|
4
4
|
description = "Cloud Dev Harness - cloud-native development Agent framework with A2TUI"
|
|
5
5
|
requires-python = ">=3.14.2"
|
|
6
6
|
dependencies = [
|
|
@@ -57,6 +57,9 @@ onecode = [
|
|
|
57
57
|
"builtin_skills/**/*",
|
|
58
58
|
]
|
|
59
59
|
|
|
60
|
+
[tool.ruff]
|
|
61
|
+
exclude = ["tui/", "dist/", ".venv/"]
|
|
62
|
+
|
|
60
63
|
[tool.pytest.ini_options]
|
|
61
64
|
asyncio_mode = "auto"
|
|
62
65
|
|
|
@@ -66,5 +69,6 @@ dev = [
|
|
|
66
69
|
"pyinstrument>=5.1.1",
|
|
67
70
|
"pytest>=9.0.3",
|
|
68
71
|
"pytest-asyncio>=1.4.0",
|
|
72
|
+
"pytest-cov>=6.0.0",
|
|
69
73
|
"textual-dev>=1.8.0",
|
|
70
74
|
]
|
package/run.js
CHANGED
|
@@ -1,6 +1,8 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
|
-
const { spawn, execSync } = require('child_process');
|
|
2
|
+
const { spawn, spawnSync, execSync } = require('child_process');
|
|
3
3
|
const path = require('path');
|
|
4
|
+
const fs = require('fs');
|
|
5
|
+
const os = require('os');
|
|
4
6
|
const pkg = require('./package.json');
|
|
5
7
|
|
|
6
8
|
function exec(cmd, opts = {}) {
|
|
@@ -11,6 +13,12 @@ function exec(cmd, opts = {}) {
|
|
|
11
13
|
}
|
|
12
14
|
}
|
|
13
15
|
|
|
16
|
+
function execVerbose(cmd, label) {
|
|
17
|
+
if (label) console.log(`cdh: ${label}`);
|
|
18
|
+
const r = spawnSync(cmd, { shell: true, stdio: 'inherit' });
|
|
19
|
+
return { ok: r.status === 0 };
|
|
20
|
+
}
|
|
21
|
+
|
|
14
22
|
function checkPython() {
|
|
15
23
|
const script = 'import sys; v=sys.version_info; print(f"{v.major}.{v.minor}")';
|
|
16
24
|
const r = exec(`python3 -c "${script}"`);
|
|
@@ -32,11 +40,49 @@ function checkCdhInstalled(pythonCmd) {
|
|
|
32
40
|
return exec(`${pythonCmd} -m pip show cloud-dev-harness 2>/dev/null`).ok;
|
|
33
41
|
}
|
|
34
42
|
|
|
43
|
+
function uninstallCmd() {
|
|
44
|
+
const cdhDir = path.join(os.homedir(), '.cdh');
|
|
45
|
+
const pythonDir = path.join(cdhDir, 'python');
|
|
46
|
+
|
|
47
|
+
console.log('cdh: Uninstalling Cloud Dev Harness...');
|
|
48
|
+
console.log('');
|
|
49
|
+
|
|
50
|
+
if (fs.existsSync(pythonDir)) {
|
|
51
|
+
console.log(`cdh: Removing Python environment at ${pythonDir}...`);
|
|
52
|
+
fs.rmSync(pythonDir, { recursive: true, force: true });
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
if (fs.existsSync(cdhDir)) {
|
|
56
|
+
console.log(`cdh: Removing global state at ${cdhDir}...`);
|
|
57
|
+
fs.rmSync(cdhDir, { recursive: true, force: true });
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
console.log('');
|
|
61
|
+
console.log('cdh: Cleanup complete. To finish uninstall:');
|
|
62
|
+
console.log('');
|
|
63
|
+
console.log(' 1. Remove the npm package:');
|
|
64
|
+
console.log(' pnpm remove -g @scemoon/cdh');
|
|
65
|
+
console.log(' # or: npm uninstall -g @scemoon/cdh');
|
|
66
|
+
console.log('');
|
|
67
|
+
console.log(' 2. Check your shell config (~/.zshrc, ~/.bashrc, etc.) for:');
|
|
68
|
+
console.log(' export PATH="$HOME/.cdh/python/bin:$PATH"');
|
|
69
|
+
console.log(' Remove this line if present.');
|
|
70
|
+
console.log('');
|
|
71
|
+
|
|
72
|
+
process.exit(0);
|
|
73
|
+
}
|
|
74
|
+
|
|
35
75
|
function run(pythonModule) {
|
|
36
76
|
const PKG_DIR = __dirname;
|
|
37
|
-
const PYTHON_ENV_DIR = path.join(
|
|
77
|
+
const PYTHON_ENV_DIR = path.join(os.homedir(), '.cdh', 'python');
|
|
38
78
|
|
|
39
79
|
const args = process.argv.slice(2);
|
|
80
|
+
|
|
81
|
+
if (args[0] === 'uninstall') {
|
|
82
|
+
uninstallCmd();
|
|
83
|
+
return;
|
|
84
|
+
}
|
|
85
|
+
|
|
40
86
|
if (args.includes('--version') || args.includes('-v')) {
|
|
41
87
|
console.log(pkg.version);
|
|
42
88
|
process.exit(0);
|
|
@@ -49,13 +95,19 @@ function run(pythonModule) {
|
|
|
49
95
|
console.error(`cdh: Python ${py.version || 'not found'}, version 3.14+ is required.`);
|
|
50
96
|
|
|
51
97
|
if (checkUv()) {
|
|
52
|
-
|
|
53
|
-
|
|
98
|
+
if (fs.existsSync(PYTHON_ENV_DIR)) {
|
|
99
|
+
console.log('cdh: Python environment exists, reusing...');
|
|
100
|
+
} else {
|
|
101
|
+
const rv = execVerbose(`uv venv "${PYTHON_ENV_DIR}"`, 'Creating Python environment with uv...');
|
|
102
|
+
if (!rv.ok) {
|
|
103
|
+
console.error('cdh: Failed to create Python environment.');
|
|
104
|
+
process.exit(1);
|
|
105
|
+
}
|
|
106
|
+
}
|
|
54
107
|
const venvPython = path.join(PYTHON_ENV_DIR, 'bin', 'python');
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
console.error('cdh: Install failed:', r.out);
|
|
108
|
+
const ri = execVerbose(`uv pip install "${PKG_DIR}" --python "${venvPython}"`, 'Installing cloud-dev-harness...');
|
|
109
|
+
if (!ri.ok) {
|
|
110
|
+
console.error('cdh: Install failed.');
|
|
59
111
|
process.exit(1);
|
|
60
112
|
}
|
|
61
113
|
if (isPostinstall) {
|
|
@@ -72,12 +124,14 @@ function run(pythonModule) {
|
|
|
72
124
|
}
|
|
73
125
|
|
|
74
126
|
if (!checkCdhInstalled(py.pythonCmd)) {
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
127
|
+
let ri;
|
|
128
|
+
if (checkUv()) {
|
|
129
|
+
ri = execVerbose(`uv pip install "${PKG_DIR}" --python "${py.pythonCmd}"`, 'Installing cloud-dev-harness...');
|
|
130
|
+
} else {
|
|
131
|
+
ri = execVerbose(`${py.pythonCmd} -m pip install "${PKG_DIR}"`, 'Installing cloud-dev-harness...');
|
|
132
|
+
}
|
|
133
|
+
if (!ri.ok) {
|
|
134
|
+
console.error('cdh: Install failed.');
|
|
81
135
|
process.exit(1);
|
|
82
136
|
}
|
|
83
137
|
}
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
package/tui/acp/agent.py
CHANGED
|
@@ -28,7 +28,6 @@ from tui.acp import messages
|
|
|
28
28
|
from tui.acp.prompt import build as build_prompt
|
|
29
29
|
from tui import messages as tui_messages
|
|
30
30
|
from tui.db import DB
|
|
31
|
-
from tui import paths
|
|
32
31
|
from tui import constants
|
|
33
32
|
from tui.answer import Answer
|
|
34
33
|
|
|
@@ -88,6 +87,7 @@ class Agent(AgentBase):
|
|
|
88
87
|
self._message_target: MessagePump | None = None
|
|
89
88
|
|
|
90
89
|
self._terminal_count: int = 0
|
|
90
|
+
self.session_load_info: dict = {}
|
|
91
91
|
|
|
92
92
|
@property
|
|
93
93
|
def command(self) -> str | None:
|
|
@@ -122,9 +122,14 @@ class Agent(AgentBase):
|
|
|
122
122
|
request: JSONRPC request object.
|
|
123
123
|
|
|
124
124
|
"""
|
|
125
|
-
|
|
125
|
+
if self._process is None:
|
|
126
|
+
logger.warning(
|
|
127
|
+
"Cannot send request %s: agent process is not running",
|
|
128
|
+
request.body,
|
|
129
|
+
)
|
|
130
|
+
return
|
|
126
131
|
|
|
127
|
-
logger.
|
|
132
|
+
logger.info("[client] send: %s", request.body)
|
|
128
133
|
if (stdin := self._process.stdin) is not None:
|
|
129
134
|
stdin.write(b"%s\n" % request.body_json)
|
|
130
135
|
|
|
@@ -542,61 +547,64 @@ class Agent(AgentBase):
|
|
|
542
547
|
if (task := asyncio.current_task()) is not None:
|
|
543
548
|
tasks.discard(task)
|
|
544
549
|
|
|
545
|
-
|
|
546
|
-
|
|
547
|
-
|
|
548
|
-
|
|
549
|
-
|
|
550
|
-
|
|
551
|
-
|
|
552
|
-
try:
|
|
553
|
-
line_str = line.decode("utf-8")
|
|
554
|
-
except Exception as error:
|
|
555
|
-
logger.error("Unable to decode utf-8 from agent: %s", error)
|
|
556
|
-
continue
|
|
557
|
-
|
|
558
|
-
logger.debug("[agent] %s", line_str.rstrip())
|
|
559
|
-
try:
|
|
560
|
-
agent_data: jsonrpc.JSONType = json.loads(line_str)
|
|
561
|
-
except Exception as error:
|
|
562
|
-
logger.error("Failed to decode JSON from agent: %s", error)
|
|
563
|
-
continue
|
|
564
|
-
|
|
565
|
-
if isinstance(agent_data, dict):
|
|
566
|
-
if "result" in agent_data or "error" in agent_data:
|
|
567
|
-
# Wait for pending notification dispatch tasks so all ACP
|
|
568
|
-
# messages are queued before the response future resolves.
|
|
569
|
-
if tasks:
|
|
570
|
-
await asyncio.gather(*tasks, return_exceptions=True)
|
|
571
|
-
tasks.clear()
|
|
572
|
-
API.process_response(agent_data)
|
|
550
|
+
try:
|
|
551
|
+
while line := await process.stdout.readline():
|
|
552
|
+
# This line should contain JSON, which may be:
|
|
553
|
+
# A) a JSONRPC request
|
|
554
|
+
# B) a JSONRPC response to a previous request
|
|
555
|
+
if not line.strip():
|
|
573
556
|
continue
|
|
574
557
|
|
|
575
|
-
|
|
576
|
-
|
|
577
|
-
|
|
558
|
+
try:
|
|
559
|
+
line_str = line.decode("utf-8")
|
|
560
|
+
except Exception as error:
|
|
561
|
+
logger.error("Unable to decode utf-8 from agent: %s", error)
|
|
578
562
|
continue
|
|
579
563
|
|
|
580
|
-
|
|
581
|
-
|
|
582
|
-
|
|
564
|
+
logger.debug("[agent] %s", line_str.rstrip())
|
|
565
|
+
try:
|
|
566
|
+
agent_data: jsonrpc.JSONType = json.loads(line_str)
|
|
567
|
+
except Exception as error:
|
|
568
|
+
logger.error("Failed to decode JSON from agent: %s", error)
|
|
569
|
+
continue
|
|
583
570
|
|
|
584
|
-
|
|
585
|
-
|
|
586
|
-
|
|
571
|
+
if isinstance(agent_data, dict):
|
|
572
|
+
if "result" in agent_data or "error" in agent_data:
|
|
573
|
+
# Wait for pending notification dispatch tasks so all ACP
|
|
574
|
+
# messages are queued before the response future resolves.
|
|
575
|
+
if tasks:
|
|
576
|
+
await asyncio.gather(*tasks, return_exceptions=True)
|
|
577
|
+
tasks.clear()
|
|
578
|
+
API.process_response(agent_data)
|
|
579
|
+
continue
|
|
580
|
+
|
|
581
|
+
elif isinstance(agent_data, list):
|
|
582
|
+
if not all(isinstance(datum, dict) for datum in agent_data):
|
|
583
|
+
logger.error("Agent sent invalid data: %r", agent_data)
|
|
584
|
+
continue
|
|
585
|
+
|
|
586
|
+
if not isinstance(agent_data, dict):
|
|
587
|
+
logger.error("Invalid JSON from agent: %r", agent_data)
|
|
588
|
+
continue
|
|
587
589
|
|
|
588
|
-
|
|
589
|
-
|
|
590
|
-
|
|
591
|
-
await asyncio.sleep(0)
|
|
590
|
+
if not isinstance(agent_data, dict):
|
|
591
|
+
logger.error("Invalid JSON from agent: %r", agent_data)
|
|
592
|
+
continue
|
|
592
593
|
|
|
593
|
-
|
|
594
|
-
|
|
595
|
-
|
|
594
|
+
# By this point we know it is a JSON RPC call
|
|
595
|
+
assert isinstance(agent_data, dict)
|
|
596
|
+
tasks.add(asyncio.create_task(call_jsonrpc(agent_data)))
|
|
597
|
+
await asyncio.sleep(0)
|
|
598
|
+
except Exception as e:
|
|
599
|
+
logger.exception("Agent read loop failed: %s", e)
|
|
600
|
+
finally:
|
|
601
|
+
# Cancel all remaining tasks and wait for them to finish
|
|
602
|
+
for task in tasks:
|
|
603
|
+
task.cancel()
|
|
596
604
|
|
|
597
|
-
|
|
598
|
-
|
|
599
|
-
|
|
605
|
+
# Wait for all tasks to complete cancellation
|
|
606
|
+
if tasks:
|
|
607
|
+
await asyncio.gather(*tasks, return_exceptions=True)
|
|
600
608
|
|
|
601
609
|
if process.returncode:
|
|
602
610
|
assert process.stderr is not None
|
|
@@ -691,6 +699,7 @@ class Agent(AgentBase):
|
|
|
691
699
|
protocol=data["protocol"],
|
|
692
700
|
meta=data["meta"],
|
|
693
701
|
)
|
|
702
|
+
self.post_message(tui_messages.SessionUpdate(session_pk=self.session_pk))
|
|
694
703
|
self._pending_session_data = None
|
|
695
704
|
await self._save_last_session_to_cdh()
|
|
696
705
|
|
|
@@ -816,8 +825,19 @@ class Agent(AgentBase):
|
|
|
816
825
|
self.post_message(messages.SessionReplay(active=True))
|
|
817
826
|
try:
|
|
818
827
|
response = await session_load_response.wait()
|
|
828
|
+
self.session_load_info = {
|
|
829
|
+
"total_messages": response.get("_total_messages", 0),
|
|
830
|
+
"visible_start": response.get("_visible_start", 0),
|
|
831
|
+
}
|
|
819
832
|
finally:
|
|
820
|
-
self.post_message(messages.SessionReplay(
|
|
833
|
+
self.post_message(messages.SessionReplay(
|
|
834
|
+
active=False,
|
|
835
|
+
total_messages=self.session_load_info.get("total_messages", 0),
|
|
836
|
+
visible_count=self.session_load_info.get("total_messages", 0) - self.session_load_info.get("visible_start", 0),
|
|
837
|
+
))
|
|
838
|
+
|
|
839
|
+
if error := response.get("error"):
|
|
840
|
+
logger.warning("Session load error: %s", error)
|
|
821
841
|
|
|
822
842
|
if (modes := response.get("modes", None)) is not None:
|
|
823
843
|
current_mode = modes["currentModeId"]
|
|
@@ -845,10 +865,20 @@ class Agent(AgentBase):
|
|
|
845
865
|
await self._ensure_db_session()
|
|
846
866
|
|
|
847
867
|
if session_title:
|
|
848
|
-
self.post_message(tui_messages.SessionUpdate(name=session_title))
|
|
868
|
+
self.post_message(tui_messages.SessionUpdate(name=session_title, session_pk=self.session_pk))
|
|
849
869
|
|
|
850
870
|
await self._save_last_session_to_cdh()
|
|
851
871
|
|
|
872
|
+
async def acp_load_earlier_messages(self, offset: int, limit: int) -> None:
|
|
873
|
+
"""Load a range of earlier messages from the current session."""
|
|
874
|
+
assert self.session_id is not None
|
|
875
|
+
with self.request():
|
|
876
|
+
response = api.session_load_earlier(self.session_id, offset, limit)
|
|
877
|
+
try:
|
|
878
|
+
await response.wait()
|
|
879
|
+
except jsonrpc.JSONRPCError:
|
|
880
|
+
pass
|
|
881
|
+
|
|
852
882
|
async def acp_session_prompt(
|
|
853
883
|
self, prompt: list[protocol.ContentBlock]
|
|
854
884
|
) -> str | None:
|
|
@@ -858,6 +888,14 @@ class Agent(AgentBase):
|
|
|
858
888
|
The stop reason.
|
|
859
889
|
|
|
860
890
|
"""
|
|
891
|
+
if self._process is None:
|
|
892
|
+
self.post_message(
|
|
893
|
+
AgentFail(
|
|
894
|
+
"Agent process is not running",
|
|
895
|
+
"The agent subprocess has terminated. Restart the session to continue.",
|
|
896
|
+
)
|
|
897
|
+
)
|
|
898
|
+
return None
|
|
861
899
|
with self.request():
|
|
862
900
|
session_prompt = api.session_prompt(prompt, self.session_id)
|
|
863
901
|
try:
|
|
@@ -908,6 +946,15 @@ class Agent(AgentBase):
|
|
|
908
946
|
async def set_mode(self, mode_id: str) -> str | None:
|
|
909
947
|
return await self.acp_session_set_mode(mode_id)
|
|
910
948
|
|
|
949
|
+
async def acp_session_clear_todos(self) -> dict:
|
|
950
|
+
"""Clear all todos via the session/clear_todos RPC."""
|
|
951
|
+
if self.session_id is None:
|
|
952
|
+
return {"cleared": False}
|
|
953
|
+
with self.request():
|
|
954
|
+
response = api.session_clear_todos(self.session_id)
|
|
955
|
+
result = await response.wait()
|
|
956
|
+
return result or {"cleared": False}
|
|
957
|
+
|
|
911
958
|
async def set_session_name(self, name: str) -> None:
|
|
912
959
|
if self.session_pk is None:
|
|
913
960
|
return
|
|
@@ -919,16 +966,36 @@ class Agent(AgentBase):
|
|
|
919
966
|
if session.load():
|
|
920
967
|
session.name = name
|
|
921
968
|
session.save()
|
|
922
|
-
self.post_message(tui_messages.SessionUpdate(name=name))
|
|
969
|
+
self.post_message(tui_messages.SessionUpdate(name=name, session_pk=self.session_pk))
|
|
923
970
|
|
|
924
971
|
async def acp_session_cancel(self) -> bool:
|
|
925
|
-
|
|
926
|
-
|
|
972
|
+
"""Send session/cancel notification to the agent subprocess.
|
|
973
|
+
|
|
974
|
+
Writes directly to stdin with drain() to ensure the message is
|
|
975
|
+
actually flushed through the pipe, unlike the Request path which
|
|
976
|
+
is synchronous and cannot drain.
|
|
977
|
+
"""
|
|
978
|
+
if not self.session_id:
|
|
979
|
+
logger.info("[client] session/cancel: no session_id, skipping")
|
|
980
|
+
return False
|
|
981
|
+
cancel_msg = {
|
|
982
|
+
"jsonrpc": "2.0",
|
|
983
|
+
"method": "session/cancel",
|
|
984
|
+
"params": {"sessionId": self.session_id, "_meta": {}},
|
|
985
|
+
}
|
|
986
|
+
data = json.dumps(cancel_msg).encode("utf-8") + b"\n"
|
|
987
|
+
logger.info("[client] session/cancel: sending %s", cancel_msg)
|
|
988
|
+
stdin = self._process.stdin if self._process else None
|
|
989
|
+
if stdin is None:
|
|
990
|
+
logger.info("[client] session/cancel: no stdin pipe, skipping")
|
|
991
|
+
return False
|
|
992
|
+
stdin.write(data)
|
|
927
993
|
try:
|
|
928
|
-
await
|
|
929
|
-
except
|
|
930
|
-
|
|
994
|
+
await stdin.drain()
|
|
995
|
+
except OSError:
|
|
996
|
+
logger.info("[client] session/cancel: drain failed", exc_info=True)
|
|
931
997
|
return False
|
|
998
|
+
logger.info("[client] session/cancel: sent successfully")
|
|
932
999
|
return True
|
|
933
1000
|
|
|
934
1001
|
async def cancel(self) -> bool:
|
package/tui/acp/api.py
CHANGED
|
@@ -59,3 +59,15 @@ def session_set_mode(sessionId: str, modeId: str) -> protocol.SetSessionModeResp
|
|
|
59
59
|
def session_save(sessionId: str) -> dict:
|
|
60
60
|
"""Save the session state."""
|
|
61
61
|
...
|
|
62
|
+
|
|
63
|
+
|
|
64
|
+
@API.method(name="session/clear_todos")
|
|
65
|
+
def session_clear_todos(sessionId: str) -> dict:
|
|
66
|
+
"""Clear all todos in the current session."""
|
|
67
|
+
...
|
|
68
|
+
|
|
69
|
+
|
|
70
|
+
@API.method(name="session/load_earlier")
|
|
71
|
+
def session_load_earlier(sessionId: str, offset: int, limit: int) -> dict:
|
|
72
|
+
"""Load a range of earlier messages from the current session."""
|
|
73
|
+
...
|
package/tui/acp/messages.py
CHANGED
|
@@ -3,7 +3,7 @@ from __future__ import annotations
|
|
|
3
3
|
from dataclasses import dataclass, field
|
|
4
4
|
|
|
5
5
|
from asyncio import Future
|
|
6
|
-
from typing import
|
|
6
|
+
from typing import Mapping, TYPE_CHECKING
|
|
7
7
|
from textual.message import Message
|
|
8
8
|
|
|
9
9
|
import rich.repr
|
|
@@ -38,6 +38,8 @@ class Thinking(AgentMessage):
|
|
|
38
38
|
class SessionReplay(AgentMessage):
|
|
39
39
|
"""Marks the start/end of a session replay (loading historical messages)."""
|
|
40
40
|
active: bool
|
|
41
|
+
total_messages: int = 0
|
|
42
|
+
visible_count: int = 0
|
|
41
43
|
|
|
42
44
|
|
|
43
45
|
@dataclass
|