add claw-code-agent/src/plan_runtime.py and claw-code-agent/src/background_runtime.py. The agent now has a real
persistent plan runtime with update_plan, plan_get, and plan_clear, plus plan-to-task sync wired through claw-code-agent/src/ agent_tools.py, claw-code-agent/src/agent_runtime.py, claw-code-agent/src/agent_context.py, claw-code-agent/src/agent_prompting.py, and claw-code-agent/src/ agent_slash_commands.py. New local plan slash commands are /plan and /planner.
This commit is contained in:
@@ -0,0 +1,109 @@
|
||||
from __future__ import annotations
|
||||
|
||||
import os
|
||||
import subprocess
|
||||
import sys
|
||||
import tempfile
|
||||
import time
|
||||
import unittest
|
||||
from pathlib import Path
|
||||
|
||||
from src.background_runtime import BackgroundSessionRuntime
|
||||
|
||||
|
||||
PROJECT_ROOT = Path(__file__).resolve().parents[1]
|
||||
|
||||
|
||||
class BackgroundRuntimeTests(unittest.TestCase):
|
||||
def test_runtime_can_launch_and_kill_generic_process(self) -> None:
|
||||
with tempfile.TemporaryDirectory() as tmp_dir:
|
||||
workspace = Path(tmp_dir)
|
||||
runtime = BackgroundSessionRuntime(workspace / '.port_sessions' / 'background')
|
||||
record = runtime.launch(
|
||||
[sys.executable, '-c', 'import time; time.sleep(10)'],
|
||||
prompt='sleep',
|
||||
workspace_cwd=workspace,
|
||||
model='local/test-model',
|
||||
process_cwd=workspace,
|
||||
)
|
||||
running = runtime.load_record(record.background_id)
|
||||
killed = runtime.kill(record.background_id)
|
||||
for _ in range(30):
|
||||
if runtime.load_record(record.background_id).status != 'running':
|
||||
break
|
||||
time.sleep(0.1)
|
||||
|
||||
self.assertEqual(running.status, 'running')
|
||||
self.assertEqual(killed.status, 'killed')
|
||||
self.assertEqual(killed.stop_reason, 'killed')
|
||||
|
||||
def test_agent_background_cli_exposes_ps_logs_and_attach(self) -> None:
|
||||
with tempfile.TemporaryDirectory() as tmp_dir:
|
||||
run_dir = Path(tmp_dir)
|
||||
workspace = run_dir / 'workspace'
|
||||
workspace.mkdir()
|
||||
env = os.environ.copy()
|
||||
existing_pythonpath = env.get('PYTHONPATH')
|
||||
env['PYTHONPATH'] = (
|
||||
f'{PROJECT_ROOT}:{existing_pythonpath}'
|
||||
if existing_pythonpath
|
||||
else str(PROJECT_ROOT)
|
||||
)
|
||||
launch = subprocess.run(
|
||||
[
|
||||
sys.executable,
|
||||
'-m',
|
||||
'src.main',
|
||||
'agent-bg',
|
||||
'/help',
|
||||
'--cwd',
|
||||
str(workspace),
|
||||
],
|
||||
cwd=run_dir,
|
||||
env=env,
|
||||
check=True,
|
||||
capture_output=True,
|
||||
text=True,
|
||||
)
|
||||
background_id = next(
|
||||
line.split('=', 1)[1]
|
||||
for line in launch.stdout.splitlines()
|
||||
if line.startswith('background_id=')
|
||||
)
|
||||
runtime = BackgroundSessionRuntime(run_dir / '.port_sessions' / 'background')
|
||||
record = runtime.load_record(background_id)
|
||||
for _ in range(60):
|
||||
if record.status in {'completed', 'failed', 'exited'}:
|
||||
break
|
||||
time.sleep(0.1)
|
||||
record = runtime.load_record(background_id)
|
||||
ps = subprocess.run(
|
||||
[sys.executable, '-m', 'src.main', 'agent-ps'],
|
||||
cwd=run_dir,
|
||||
env=env,
|
||||
check=True,
|
||||
capture_output=True,
|
||||
text=True,
|
||||
)
|
||||
logs = subprocess.run(
|
||||
[sys.executable, '-m', 'src.main', 'agent-logs', background_id],
|
||||
cwd=run_dir,
|
||||
env=env,
|
||||
check=True,
|
||||
capture_output=True,
|
||||
text=True,
|
||||
)
|
||||
attach = subprocess.run(
|
||||
[sys.executable, '-m', 'src.main', 'agent-attach', background_id],
|
||||
cwd=run_dir,
|
||||
env=env,
|
||||
check=True,
|
||||
capture_output=True,
|
||||
text=True,
|
||||
)
|
||||
|
||||
self.assertIn(background_id, launch.stdout)
|
||||
self.assertIn(background_id, ps.stdout)
|
||||
self.assertIn('# Background Logs', logs.stdout)
|
||||
self.assertIn('# Slash Commands', logs.stdout)
|
||||
self.assertIn('# Background Attach', attach.stdout)
|
||||
Reference in New Issue
Block a user