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:
Abdelrahman Abdallah
2026-04-03 18:31:25 +02:00
parent 045413e5e1
commit 3f31cee395
23 changed files with 4344 additions and 93 deletions
+164
View File
@@ -2,11 +2,13 @@ from __future__ import annotations
import argparse
import os
import sys
from pathlib import Path
from dataclasses import replace
import json
from typing import Callable
from .background_runtime import BackgroundSessionRuntime, build_background_worker_command
from .agent_runtime import LocalCodingAgent
from .agent_types import (
AgentPermissions,
@@ -154,6 +156,64 @@ def _build_agent(args: argparse.Namespace) -> LocalCodingAgent:
)
def _append_agent_forwarded_args(
command: list[str],
args: argparse.Namespace,
*,
include_backend: bool,
) -> None:
command.extend(['--cwd', str(args.cwd)])
command.extend(['--max-turns', str(getattr(args, 'max_turns', 12))])
if include_backend:
command.extend(['--model', str(args.model)])
command.extend(['--base-url', str(args.base_url)])
command.extend(['--api-key', str(args.api_key)])
command.extend(['--temperature', str(args.temperature)])
command.extend(['--timeout-seconds', str(args.timeout_seconds)])
command.extend(['--input-cost-per-million', str(args.input_cost_per_million)])
command.extend(['--output-cost-per-million', str(args.output_cost_per_million)])
else:
command.extend(['--model', str(args.model)])
for path in getattr(args, 'add_dir', []):
command.extend(['--add-dir', str(path)])
for flag in (
('--disable-claude-md', getattr(args, 'disable_claude_md', False)),
('--allow-write', getattr(args, 'allow_write', False)),
('--allow-shell', getattr(args, 'allow_shell', False)),
('--unsafe', getattr(args, 'unsafe', False)),
('--stream', getattr(args, 'stream', False)),
('--show-transcript', getattr(args, 'show_transcript', False)),
(
'--response-schema-strict',
getattr(args, 'response_schema_strict', False),
),
):
if flag[1]:
command.append(flag[0])
for name, value in (
('--auto-snip-threshold', getattr(args, 'auto_snip_threshold', None)),
('--auto-compact-threshold', getattr(args, 'auto_compact_threshold', None)),
('--compact-preserve-messages', getattr(args, 'compact_preserve_messages', None)),
('--max-total-tokens', getattr(args, 'max_total_tokens', None)),
('--max-input-tokens', getattr(args, 'max_input_tokens', None)),
('--max-output-tokens', getattr(args, 'max_output_tokens', None)),
('--max-reasoning-tokens', getattr(args, 'max_reasoning_tokens', None)),
('--max-budget-usd', getattr(args, 'max_budget_usd', None)),
('--max-tool-calls', getattr(args, 'max_tool_calls', None)),
('--max-delegated-tasks', getattr(args, 'max_delegated_tasks', None)),
('--max-model-calls', getattr(args, 'max_model_calls', None)),
('--max-session-turns', getattr(args, 'max_session_turns', None)),
('--response-schema-file', getattr(args, 'response_schema_file', None)),
('--response-schema-name', getattr(args, 'response_schema_name', None)),
('--scratchpad-root', getattr(args, 'scratchpad_root', None)),
('--system-prompt', getattr(args, 'system_prompt', None)),
('--append-system-prompt', getattr(args, 'append_system_prompt', None)),
('--override-system-prompt', getattr(args, 'override_system_prompt', None)),
):
if value is not None:
command.extend([name, str(value)])
def _add_agent_resume_args(parser: argparse.ArgumentParser) -> None:
parser.add_argument('session_id')
parser.add_argument('prompt')
@@ -487,6 +547,34 @@ def build_parser() -> argparse.ArgumentParser:
agent_parser.add_argument('--show-transcript', action='store_true')
_add_agent_common_args(agent_parser, include_backend=True)
background_parser = subparsers.add_parser('agent-bg', help='run the Python local-model agent as a local background session')
background_parser.add_argument('prompt')
background_parser.add_argument('--max-turns', type=int, default=12)
background_parser.add_argument('--show-transcript', action='store_true')
_add_agent_common_args(background_parser, include_backend=True)
background_worker_parser = subparsers.add_parser('agent-bg-worker', help=argparse.SUPPRESS)
background_worker_parser.add_argument('background_id')
background_worker_parser.add_argument('prompt')
background_worker_parser.add_argument('--background-root', required=True)
background_worker_parser.add_argument('--max-turns', type=int, default=12)
background_worker_parser.add_argument('--show-transcript', action='store_true')
_add_agent_common_args(background_worker_parser, include_backend=True)
ps_parser = subparsers.add_parser('agent-ps', help='list local background agent sessions')
ps_parser.add_argument('--tail', type=int, default=None)
logs_parser = subparsers.add_parser('agent-logs', help='show logs for a local background agent session')
logs_parser.add_argument('background_id')
logs_parser.add_argument('--tail', type=int, default=None)
attach_parser = subparsers.add_parser('agent-attach', help='show the current output snapshot for a local background agent session')
attach_parser.add_argument('background_id')
attach_parser.add_argument('--tail', type=int, default=None)
kill_parser = subparsers.add_parser('agent-kill', help='stop a local background agent session')
kill_parser.add_argument('background_id')
chat_parser = subparsers.add_parser('agent-chat', help='run an interactive Python local-model chat loop')
chat_parser.add_argument('prompt', nargs='?')
chat_parser.add_argument('--resume-session-id')
@@ -640,6 +728,82 @@ def main(argv: list[str] | None = None) -> int:
result = agent.run(args.prompt)
_print_agent_result(result, show_transcript=args.show_transcript)
return 0
if args.command == 'agent-bg':
background_runtime = BackgroundSessionRuntime()
background_id = background_runtime.create_id()
forwarded_args: list[str] = []
_append_agent_forwarded_args(forwarded_args, args, include_backend=True)
forwarded_args.extend(['--background-root', str(background_runtime.root)])
command = build_background_worker_command(
background_id=background_id,
prompt=args.prompt,
forwarded_args=forwarded_args,
)
record = background_runtime.launch(
command,
prompt=args.prompt,
workspace_cwd=Path(args.cwd).resolve(),
model=args.model,
background_id=background_id,
process_cwd=Path(__file__).resolve().parent.parent,
)
print('# Background Session')
print(f'background_id={record.background_id}')
print(f'pid={record.pid}')
print(f'log_path={record.log_path}')
print(f'record_path={record.record_path}')
return 0
if args.command == 'agent-bg-worker':
background_runtime = BackgroundSessionRuntime(Path(args.background_root))
exit_code = 1
stop_reason = 'worker_failed'
session_id = None
session_path = None
try:
agent = _build_agent(args)
result = agent.run(args.prompt)
_print_agent_result(result, show_transcript=args.show_transcript)
exit_code = 0
stop_reason = result.stop_reason or 'completed'
session_id = result.session_id
session_path = result.session_path
return 0
finally:
background_runtime.mark_finished(
args.background_id,
exit_code=exit_code,
stop_reason=stop_reason,
session_id=session_id,
session_path=session_path,
)
if args.command == 'agent-ps':
print(BackgroundSessionRuntime().render_ps())
return 0
if args.command == 'agent-logs':
print(
BackgroundSessionRuntime().render_logs(
args.background_id,
tail=args.tail,
)
)
return 0
if args.command == 'agent-attach':
print(
BackgroundSessionRuntime().render_attach(
args.background_id,
tail=args.tail,
)
)
return 0
if args.command == 'agent-kill':
record = BackgroundSessionRuntime().kill(args.background_id)
print('# Background Session')
print(f'background_id={record.background_id}')
print(f'status={record.status}')
print(f'pid={record.pid}')
if record.exit_code is not None:
print(f'exit_code={record.exit_code}')
return 0
if args.command == 'agent-chat':
agent = _build_agent(args)
return _run_agent_chat_loop(