Skip to content

Commit 76e013e

Browse files
author
Kun
committed
Add local skills, durable tasks, and bounded subagents
Stage 6 adds the Option B-min slice from the cc product roadmap: local SKILL.md loading, a store-backed durable task graph, and a synchronous stateless run_subagent tool with an exact child-tool allowlist. The implementation keeps richer Claude Code agent runtime concepts deferred while wiring the new surfaces through the existing LangChain create_agent tool system. Constraint: LangChain-first implementation; no custom query loop, background agents, mailbox, worktrees, remote/team runtime, sidechain resume, or forked skill execution. Constraint: TodoWrite remains session-local and separate from durable task records. Rejected: Full AgentTool parity now | would require background/resume/mailbox/worktree behavior outside the approved Option B-min slice. Rejected: Plugin/MCP skill loading now | Stage 6 is local skills only and keeps extension platform work for a later stage. Confidence: high Scope-risk: moderate Reversibility: clean Directive: Future multi-agent work must explicitly expand the subagent contract before adding mailbox/background/worktree/resume semantics. Tested: cd coding-deepgent && python -m pytest -q -> 87 passed Tested: cd coding-deepgent && ruff check . -> all checks passed Tested: cd coding-deepgent && ruff format --check . -> 107 files already formatted Tested: cd coding-deepgent && python -m mypy src/coding_deepgent tests -> success Tested: forbidden runtime-creep grep guards -> passed Tested: Architect verification -> APPROVE Not-tested: real background/remote subagent execution; intentionally deferred.
1 parent 26c1a77 commit 76e013e

29 files changed

Lines changed: 851 additions & 13 deletions

coding-deepgent/PROJECT_PROGRESS.md

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -2,8 +2,8 @@
22

33
## Current product stage
44

5-
- `current_product_stage`: `stage-5-memory-context-compact-foundation`
6-
- `compatibility_anchor`: `memory-context-compact-foundation`
5+
- `current_product_stage`: `stage-6-skills-subagents-task-graph`
6+
- `compatibility_anchor`: `skills-subagents-task-graph`
77
- Status: control-plane foundation implemented as one cumulative LangChain cc product surface
88
- Last updated: 2026-04-12
99

@@ -34,3 +34,7 @@ Stage 4 adds deterministic permission/safety decisions, local lifecycle hooks, a
3434
## Stage 5 memory/context/compact foundation
3535

3636
Stage 5 adds a store-backed long-term memory foundation seam, the model-visible `save_memory` tool, bounded memory context injection, and deterministic tool-result budget helpers. Message-history projection/pruning, LLM autocompact, session-memory side-agent writing, subagents, durable tasks, and MCP/plugin memory sync remain future work.
37+
38+
## Stage 6 skills/subagents/task graph
39+
40+
Stage 6 adds local skill loading, a store-backed durable task graph, and a minimal synchronous/stateless `run_subagent` tool. Background agents, SendMessage/mailbox, worktrees, remote/team runtime, sidechain resume, forked skill execution, MCP/plugin marketplace, and custom query loops remain future work.

coding-deepgent/README.md

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -4,8 +4,8 @@ Independent cumulative LangChain cc product surface.
44

55
## Current product stage
66

7-
- `current_product_stage`: `stage-5-memory-context-compact-foundation`
8-
- `compatibility_anchor`: `memory-context-compact-foundation`
7+
- `current_product_stage`: `stage-6-skills-subagents-task-graph`
8+
- `compatibility_anchor`: `skills-subagents-task-graph`
99
- Upgrade policy: advance by explicit product-stage plan approval, not tutorial chapter completion.
1010

1111
## Current architecture
@@ -35,3 +35,7 @@ Stage 4 adds deterministic permission/safety decisions, local lifecycle hooks, a
3535
## Stage 5 memory/context/compact foundation
3636

3737
Stage 5 adds a store-backed long-term memory foundation seam, the model-visible `save_memory` tool, bounded memory context injection, and deterministic tool-result budget helpers. Message-history projection/pruning, LLM autocompact, session-memory side-agent writing, subagents, durable tasks, and MCP/plugin memory sync remain future work.
38+
39+
## Stage 6 skills/subagents/task graph
40+
41+
Stage 6 adds local skill loading, a store-backed durable task graph, and a minimal synchronous/stateless `run_subagent` tool. Background agents, SendMessage/mailbox, worktrees, remote/team runtime, sidechain resume, forked skill execution, MCP/plugin marketplace, and custom query loops remain future work.

coding-deepgent/docs/cc-alignment-roadmap.md

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -32,6 +32,13 @@ Aligning Stage 4 should improve safety, maintainability, context quality, and te
3232
- `/root/claude-code-haha/src/utils/queryContext.ts:fetchSystemPromptParts` -> prompt builder accepts rendered memory context as a distinct prompt section -> align -> use existing `create_agent` prompt path.
3333
- `/root/claude-code-haha/src/query.ts:applyToolResultBudget` -> deterministic `apply_tool_result_budget()` helper for oversized tool-result strings -> partial -> no message-history projection/pruning in Stage 5.
3434

35+
## Stage 6 skills/tasks/subagents rows
36+
37+
- `/root/claude-code-haha/src/tools/SkillTool/SkillTool.ts` -> local `load_skill` tool loads one `SKILL.md` by explicit name -> partial -> no plugin/MCP/remote skills.
38+
- `/root/claude-code-haha/src/utils/tasks.ts` and `/root/claude-code-haha/src/tools/Task*Tool/*` -> store-backed `task_create/get/list/update` with strict transitions -> partial -> no coordinator/team runtime yet.
39+
- `/root/claude-code-haha/src/tools/AgentTool/AgentTool.tsx` -> minimal synchronous/stateless `run_subagent` tool with exact child-tool allowlist -> partial -> no background/worktree/mailbox/resume.
40+
- `/root/claude-code-haha/src/tools/SendMessageTool/*` -> mailbox/send-message semantics -> defer -> requires later multi-agent runtime.
41+
3542
## Next candidates
3643

3744
1. Stage 5: memory + context budget + compact seam.

coding-deepgent/project_status.json

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
{
22
"project": "coding-deepgent",
3-
"current_product_stage": "stage-5-memory-context-compact-foundation",
4-
"compatibility_anchor": "memory-context-compact-foundation",
3+
"current_product_stage": "stage-6-skills-subagents-task-graph",
4+
"compatibility_anchor": "skills-subagents-task-graph",
55
"shape": "staged_langchain_cc_product",
66
"public_shape": "single cumulative app",
77
"upgrade_policy": "Advance by explicit product-stage plan approval, not tutorial chapter completion.",

coding-deepgent/src/coding_deepgent/containers/tool_system.py

Lines changed: 21 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,9 @@
77

88
from coding_deepgent.memory import save_memory
99
from coding_deepgent.permissions import PermissionManager
10+
from coding_deepgent.skills import load_skill
11+
from coding_deepgent.subagents import run_subagent
12+
from coding_deepgent.tasks import task_create, task_get, task_list, task_update
1013
from coding_deepgent.tool_system import (
1114
CapabilityRegistry,
1215
ToolCapability,
@@ -34,6 +37,12 @@ def _tool_domain(name: str) -> str:
3437
return "todo"
3538
if name == "save_memory":
3639
return "memory"
40+
if name == "load_skill":
41+
return "skills"
42+
if name.startswith("task_"):
43+
return "tasks"
44+
if name == "run_subagent":
45+
return "subagents"
3746
if name in READ_ONLY_TOOL_NAMES | DESTRUCTIVE_TOOL_NAMES:
3847
return "filesystem"
3948
return "unknown"
@@ -63,11 +72,22 @@ class ToolSystemContainer(containers.DeclarativeContainer):
6372
filesystem_tools: Any = providers.Dependency(default=providers.Object([]))
6473
todo_tools: Any = providers.Dependency(default=providers.Object([]))
6574
memory_tools: Any = providers.Dependency(default=providers.Object([save_memory]))
75+
skill_tools: Any = providers.Dependency(default=providers.Object([load_skill]))
76+
task_tools: Any = providers.Dependency(
77+
default=providers.Object([task_create, task_get, task_list, task_update])
78+
)
79+
subagent_tools: Any = providers.Dependency(default=providers.Object([run_subagent]))
6680
permission_mode: Any = providers.Dependency(default=providers.Object("default"))
6781
event_sink: Any = providers.Dependency(default=providers.Object(None))
6882

6983
tools: Any = providers.Callable(
70-
_combine_tools, filesystem_tools, todo_tools, memory_tools
84+
_combine_tools,
85+
filesystem_tools,
86+
todo_tools,
87+
memory_tools,
88+
skill_tools,
89+
task_tools,
90+
subagent_tools,
7191
)
7292
capability_registry: Any = providers.Callable(_capability_registry, tools)
7393
permission_manager: Any = providers.Factory(

coding-deepgent/src/coding_deepgent/memory/schemas.py

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,8 @@
11
from __future__ import annotations
22

33
from typing import Literal
4+
5+
from langchain.tools import ToolRuntime
46
from pydantic import BaseModel, ConfigDict, Field, field_validator
57

68
MemoryNamespace = Literal["project", "user", "local"]
@@ -25,7 +27,7 @@ def _text_must_not_be_blank(cls, value: str) -> str:
2527

2628

2729
class SaveMemoryInput(BaseModel):
28-
model_config = ConfigDict(extra="forbid")
30+
model_config = ConfigDict(extra="forbid", arbitrary_types_allowed=True)
2931

3032
content: str = Field(
3133
...,
@@ -39,6 +41,7 @@ class SaveMemoryInput(BaseModel):
3941
source: str = Field(
4042
default="agent", description="Source label for this memory entry."
4143
)
44+
runtime: ToolRuntime
4245

4346
@field_validator("content", "source")
4447
@classmethod

coding-deepgent/src/coding_deepgent/memory/tools.py

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,7 @@
1414

1515
@tool(
1616
"save_memory",
17+
args_schema=SaveMemoryInput,
1718
description=(
1819
"Save durable reusable knowledge or preferences as long-term memory. "
1920
"Do not save transient todos, current plans, task status, or one-off observations."
@@ -28,7 +29,10 @@ def save_memory(
2829
"""Save reusable long-term memory through the LangGraph store seam."""
2930

3031
validated = SaveMemoryInput(
31-
content=content, namespace=cast(MemoryNamespace, namespace), source=source
32+
content=content,
33+
namespace=cast(MemoryNamespace, namespace),
34+
source=source,
35+
runtime=runtime,
3236
)
3337
store = runtime.store
3438
if store is None:

coding-deepgent/src/coding_deepgent/runtime/context.py

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -12,4 +12,5 @@ class RuntimeContext:
1212
workdir: Path
1313
entrypoint: str
1414
agent_name: str
15+
skill_dir: Path
1516
event_sink: RuntimeEventSink

coding-deepgent/src/coding_deepgent/runtime/invocation.py

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -35,6 +35,7 @@ def build_runtime_context(
3535
workdir=settings.workdir,
3636
entrypoint=entrypoint or settings.entrypoint,
3737
agent_name=agent_name or settings.agent_name,
38+
skill_dir=settings.skill_dir,
3839
event_sink=event_sink,
3940
)
4041

coding-deepgent/src/coding_deepgent/settings.py

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -48,6 +48,7 @@ class Settings(BaseSettings):
4848

4949
workdir: Path = Field(default_factory=resolve_workdir)
5050
session_dir: Path = Field(default=Path(".coding-deepgent/sessions"))
51+
skill_dir: Path = Field(default=Path("skills"))
5152
model_name: str = Field(default_factory=deepgent_model_name)
5253
openai_api_key: SecretStr | None = Field(default=None, alias="OPENAI_API_KEY")
5354
openai_base_url: str | None = Field(default=None, alias="OPENAI_BASE_URL")
@@ -83,6 +84,11 @@ def _normalize_paths(self) -> "Settings":
8384
self.session_dir = (self.workdir / self.session_dir).resolve()
8485
else:
8586
self.session_dir = self.session_dir.expanduser().resolve()
87+
88+
if not self.skill_dir.is_absolute():
89+
self.skill_dir = (self.workdir / self.skill_dir).resolve()
90+
else:
91+
self.skill_dir = self.skill_dir.expanduser().resolve()
8692
return self
8793

8894

0 commit comments

Comments
 (0)