Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 0 additions & 1 deletion CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -52,7 +52,6 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
- Fixed repeated generation of GitHub CLI installation tokens by caching the token in the agent session state for subsequent `gh` tool calls.
- Fixed skill tool to properly return a `Command` object for state updates instead of returning messages directly.
- Fixed `daiv-auto` label to work as a trigger label that both launches the agent and enables auto-approval mode, eliminating the need to add two separate labels.
- Fixed agent post-run failures when git push returns authentication/permission errors by handling push permission failures gracefully in git middleware and adding regression tests.

### Removed

Expand Down
4 changes: 4 additions & 0 deletions daiv/automation/agent/constants.py
Original file line number Diff line number Diff line change
Expand Up @@ -39,6 +39,10 @@ class ModelName(StrEnum):

# z-ai models
Z_AI_GLM_4_7 = "openrouter:z-ai/glm-4.7"
Z_AI_GLM_5 = "openrouter:z-ai/glm-5"

# minimax models
MINIMAX_M2_5 = "openrouter:minimax/minimax-m2.5"

# MoonshotAI models
MOONSHOTAI_KIMI_K2_5 = "openrouter:moonshotai/kimi-k2.5"
71 changes: 25 additions & 46 deletions daiv/automation/agent/graph.py
Original file line number Diff line number Diff line change
Expand Up @@ -6,12 +6,14 @@
from django.utils import timezone

from deepagents.backends.filesystem import FilesystemBackend
from deepagents.graph import BASE_AGENT_PROMPT
from deepagents.middleware.memory import MemoryMiddleware
from deepagents.middleware.patch_tool_calls import PatchToolCallsMiddleware
from deepagents.middleware.subagents import SubAgentMiddleware
from deepagents.middleware.summarization import _compute_summarization_defaults
from langchain.agents import create_agent
from langchain.agents.middleware import (
HumanInTheLoopMiddleware,
InterruptOnConfig,
ModelFallbackMiddleware,
ModelRequest,
SummarizationMiddleware,
Expand Down Expand Up @@ -54,10 +56,6 @@
from langgraph.store.base import BaseStore


DEFAULT_SUMMARIZATION_TRIGGER = ("tokens", 170000)
DEFAULT_SUMMARIZATION_KEEP = ("messages", 6)


OUTPUT_INVARIANTS_SYSTEM_PROMPT = """\
<output_invariants>
Applies to ALL user-visible text:
Expand Down Expand Up @@ -93,15 +91,7 @@ async def dynamic_daiv_system_prompt(request: ModelRequest) -> str:
bash_tool_enabled=BASH_TOOL_NAME in tool_names,
working_directory=f"/{agent_path.name}/",
)
return (
BASE_AGENT_PROMPT
+ "\n\n"
+ OUTPUT_INVARIANTS_SYSTEM_PROMPT
+ "\n\n"
+ request.system_prompt
+ "\n\n"
+ system_prompt.content.strip()
)
return OUTPUT_INVARIANTS_SYSTEM_PROMPT + "\n\n" + request.system_prompt + "\n\n" + system_prompt.content.strip()


def dynamic_write_todos_system_prompt(bash_tool_enabled: bool) -> str:
Expand All @@ -121,6 +111,7 @@ async def create_daiv_agent(
store: BaseStore | None = None,
debug: bool = False,
offline: bool = False,
interrupt_on: dict[str, bool | InterruptOnConfig] | None = None,
):
"""
Create the DAIV agent.
Expand All @@ -138,34 +129,24 @@ async def create_daiv_agent(
Returns:
The DAIV agent.
"""
agent_path = Path(ctx.repo.working_dir)

model = BaseAgent.get_model(model=model_names[0], thinking_level=thinking_level)
fallback_models = [
BaseAgent.get_model(model=model_name, thinking_level=thinking_level) for model_name in model_names[1:]
]

summarization_trigger = DEFAULT_SUMMARIZATION_TRIGGER
summarization_keep = DEFAULT_SUMMARIZATION_KEEP

if isinstance(model.profile, dict) and isinstance(model.profile.get("max_input_tokens"), int):
summarization_trigger = ("fraction", 0.85)
summarization_keep = ("fraction", 0.10)
summarization_defaults = _compute_summarization_defaults(model)

agent_path = Path(ctx.repo.working_dir)
backend = FilesystemBackend(root_dir=agent_path.parent, virtual_mode=True)

subagent_default_middlewares = [
SummarizationMiddleware(
model=model, trigger=summarization_trigger, keep=summarization_keep, trim_tokens_to_summarize=None
),
AnthropicPromptCachingMiddleware(),
ToolCallLoggingMiddleware(),
PatchToolCallsMiddleware(),
# Create subagents list to be shared between middlewares
subagents = [
create_general_purpose_subagent(model, backend, ctx, offline=offline),
create_explore_subagent(backend, ctx),
create_changelog_subagent(model, backend, ctx, offline=offline),
]

if fallback_models:
subagent_default_middlewares.append(ModelFallbackMiddleware(fallback_models[0], *fallback_models[1:]))

agent_conditional_middlewares = []

if not offline:
Expand All @@ -176,13 +157,8 @@ async def create_daiv_agent(
agent_conditional_middlewares.append(SandboxMiddleware())
if fallback_models:
agent_conditional_middlewares.append(ModelFallbackMiddleware(fallback_models[0], *fallback_models[1:]))

# Create subagents list to be shared between middlewares
subagents = [
create_general_purpose_subagent(backend, ctx, offline=offline),
create_explore_subagent(backend, ctx),
create_changelog_subagent(backend, ctx),
]
if interrupt_on is not None:
agent_conditional_middlewares.append(HumanInTheLoopMiddleware(interrupt_on=interrupt_on))

agent_middleware = [
TodoListMiddleware(
Expand All @@ -195,18 +171,18 @@ async def create_daiv_agent(
SkillsMiddleware(
backend=backend, sources=[f"/{agent_path.name}/{source}" for source in SKILLS_SOURCES], subagents=subagents
),
SubAgentMiddleware(
default_model=model,
default_middleware=subagent_default_middlewares,
general_purpose_agent=False,
subagents=subagents,
),
SubAgentMiddleware(backend=backend, subagents=subagents),
*agent_conditional_middlewares,
FilesystemMiddleware(backend=backend),
GitMiddleware(auto_commit_changes=auto_commit_changes),
GitPlatformMiddleware(git_platform=ctx.git_platform),
SummarizationMiddleware(
model=model, trigger=summarization_trigger, keep=summarization_keep, trim_tokens_to_summarize=None
model=model,
backend=backend,
trigger=summarization_defaults["trigger"],
keep=summarization_defaults["keep"],
trim_tokens_to_summarize=None,
truncate_args_settings=summarization_defaults["truncate_args_settings"],
),
AnthropicPromptCachingMiddleware(),
ToolCallLoggingMiddleware(),
Expand Down Expand Up @@ -239,7 +215,10 @@ async def main():
)
async with set_runtime_ctx(repo_id="srtab/daiv", scope=Scope.GLOBAL, ref="main") as ctx:
agent = await create_daiv_agent(
ctx=ctx, model_names=["openrouter:z-ai/glm-5"], store=InMemoryStore(), checkpointer=InMemorySaver()
ctx=ctx,
model_names=["openrouter:minimax/minimax-m2.5"],
store=InMemoryStore(),
checkpointer=InMemorySaver(),
)
while True:
user_input = await session.prompt_async()
Expand Down
6 changes: 3 additions & 3 deletions daiv/automation/agent/skills/skill-creator/SKILL.md
Original file line number Diff line number Diff line change
Expand Up @@ -23,10 +23,10 @@ equipped with procedural knowledge and domain expertise.

### Skill Location for DAIV

In DAIV, skills are stored in `~/.daiv/skills/` directory. For example, with the default configuration, skills live at:
In DAIV, skills are stored in `~/.agents/skills/` directory. For example, with the default configuration, skills live at:

```
~/.daiv/skills/
~/.agents/skills/
├── skill-name-1/
│ └── SKILL.md
├── skill-name-2/
Expand Down Expand Up @@ -282,7 +282,7 @@ scripts/init_skill.py <skill-name> --path <output-directory>
For DAIV, use the DAIV's skills directory:

```bash
scripts/init_skill.py <skill-name> --path ~/.daiv/skills
scripts/init_skill.py <skill-name> --path ~/.agents/skills
```

The script:
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@
init_skill.py custom-skill --path /custom/location

For DAIV:
init_skill.py my-skill --path ~/.daiv/skills
init_skill.py my-skill --path ~/.agents/skills
"""
# ruff: NOQA: T201,E501

Expand Down Expand Up @@ -283,7 +283,7 @@ def main():
print(" init_skill.py my-api-helper --path skills/private")
print(" init_skill.py custom-skill --path /custom/location")
print("\nFor DAIV:")
print(" init_skill.py my-skill --path ~/.daiv/skills")
print(" init_skill.py my-skill --path ~/.agents/skills")
sys.exit(1)

skill_name = sys.argv[1]
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,10 +3,10 @@
Quick validation script for skills - minimal version

For DAIV, skills are located at:
~/.daiv/skills/<skill-name>/
~/.agents/skills/<skill-name>/

Example:
python quick_validate.py ~/.daiv/skills/my-skill
python quick_validate.py ~/.agents/skills/my-skill
"""
# ruff: NOQA: T201

Expand Down
66 changes: 62 additions & 4 deletions daiv/automation/agent/subagents.py
Original file line number Diff line number Diff line change
@@ -1,17 +1,23 @@
from typing import TYPE_CHECKING

from deepagents.graph import SubAgent
from deepagents.middleware import SummarizationMiddleware
from deepagents.middleware.patch_tool_calls import PatchToolCallsMiddleware
from deepagents.middleware.summarization import _compute_summarization_defaults
from langchain.agents.middleware import TodoListMiddleware

from automation.agent import BaseAgent
from automation.agent.conf import settings
from automation.agent.middlewares.file_system import FilesystemMiddleware
from automation.agent.middlewares.git_platform import GitPlatformMiddleware
from automation.agent.middlewares.logging import ToolCallLoggingMiddleware
from automation.agent.middlewares.prompt_cache import AnthropicPromptCachingMiddleware
from automation.agent.middlewares.sandbox import SandboxMiddleware
from automation.agent.middlewares.web_search import WebSearchMiddleware

if TYPE_CHECKING:
from deepagents.backends import BackendProtocol
from langchain.chat_models import BaseChatModel

from codebase.context import RuntimeCtx

Expand Down Expand Up @@ -205,18 +211,33 @@
Do NOT specify WHAT to write—let the agent examine the diffs and infer user-facing changes. The agent will handle the entire changelog update workflow and return confirmation when complete.""" # noqa: E501


def create_general_purpose_subagent(backend: BackendProtocol, runtime: RuntimeCtx, offline: bool = False) -> SubAgent:
def create_general_purpose_subagent(
model: BaseChatModel, backend: BackendProtocol, runtime: RuntimeCtx, offline: bool = False
) -> SubAgent:
"""
Create the general purpose subagent for the DAIV agent.
"""
from automation.agent.graph import dynamic_write_todos_system_prompt

summarization_defaults = _compute_summarization_defaults(model)

middleware = [
TodoListMiddleware(
system_prompt=dynamic_write_todos_system_prompt(bash_tool_enabled=runtime.config.sandbox.enabled)
),
FilesystemMiddleware(backend=backend),
GitPlatformMiddleware(git_platform=runtime.git_platform),
SummarizationMiddleware(
model=model,
backend=backend,
trigger=summarization_defaults["trigger"],
keep=summarization_defaults["keep"],
trim_tokens_to_summarize=None,
truncate_args_settings=summarization_defaults["truncate_args_settings"],
),
AnthropicPromptCachingMiddleware(),
ToolCallLoggingMiddleware(),
PatchToolCallsMiddleware(),
]

if not offline:
Expand All @@ -230,6 +251,8 @@ def create_general_purpose_subagent(backend: BackendProtocol, runtime: RuntimeCt
description=GENERAL_PURPOSE_DESCRIPTION,
system_prompt=GENERAL_PURPOSE_SYSTEM_PROMPT,
middleware=middleware,
model=model,
tools=[],
)


Expand All @@ -239,25 +262,58 @@ def create_explore_subagent(backend: BackendProtocol, runtime: RuntimeCtx) -> Su
"""
from automation.agent.graph import dynamic_write_todos_system_prompt

model = BaseAgent.get_model(model=settings.EXPLORE_MODEL_NAME)
summarization_defaults = _compute_summarization_defaults(model)

middleware = [
TodoListMiddleware(system_prompt=dynamic_write_todos_system_prompt(bash_tool_enabled=False)),
FilesystemMiddleware(backend=backend, read_only=True),
SummarizationMiddleware(
model=model,
backend=backend,
trigger=summarization_defaults["trigger"],
keep=summarization_defaults["keep"],
trim_tokens_to_summarize=None,
truncate_args_settings=summarization_defaults["truncate_args_settings"],
),
AnthropicPromptCachingMiddleware(),
ToolCallLoggingMiddleware(),
PatchToolCallsMiddleware(),
]

return SubAgent(
name="explore",
description=EXPLORE_SUBAGENT_DESCRIPTION,
system_prompt=EXPLORE_SYSTEM_PROMPT,
middleware=middleware,
model=BaseAgent.get_model(model=settings.EXPLORE_MODEL_NAME),
model=model,
tools=[],
)


def create_changelog_subagent(backend: BackendProtocol, runtime: RuntimeCtx, offline: bool = False) -> SubAgent:
def create_changelog_subagent(
model: BaseChatModel, backend: BackendProtocol, runtime: RuntimeCtx, offline: bool = False
) -> SubAgent:
"""
Create the changelog subagent.
"""
middleware = [FilesystemMiddleware(backend=backend), GitPlatformMiddleware(git_platform=runtime.git_platform)]
summarization_defaults = _compute_summarization_defaults(model)

middleware = [
FilesystemMiddleware(backend=backend),
GitPlatformMiddleware(git_platform=runtime.git_platform),
SummarizationMiddleware(
model=model,
backend=backend,
trigger=summarization_defaults["trigger"],
keep=summarization_defaults["keep"],
trim_tokens_to_summarize=None,
truncate_args_settings=summarization_defaults["truncate_args_settings"],
),
AnthropicPromptCachingMiddleware(),
ToolCallLoggingMiddleware(),
PatchToolCallsMiddleware(),
]

if not offline:
middleware.append(WebSearchMiddleware())
Expand All @@ -270,4 +326,6 @@ def create_changelog_subagent(backend: BackendProtocol, runtime: RuntimeCtx, off
description=CHANGELOG_SUBAGENT_DESCRIPTION,
system_prompt=CHANGELOG_SYSTEM_PROMPT,
middleware=middleware,
model=model,
tools=[],
)
2 changes: 1 addition & 1 deletion docs/ai-agents/overview.md
Original file line number Diff line number Diff line change
Expand Up @@ -112,7 +112,7 @@ Agents can execute commands in isolated sandbox environments using [daiv-sandbox
Agents can leverage [Agent Skills](skills.md) for specialized domain knowledge:

- **Progressive Disclosure**: Skills metadata loads at startup, full instructions load on-demand
- **Custom Skills**: Create repository-specific Skills in `.daiv/skills/`
- **Custom Skills**: Create repository-specific Skills in `.agents/skills/`
- **Builtin Skills**: Pre-packaged Skills for common tasks (e.g. AGENTS.md generation)
- **Scoped Skills**: Target Skills to specific contexts (issues or merge requests)

Expand Down
Loading
Loading