Skip to content
Closed
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 app_server/__main__.py
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,6 @@
import time
from pathlib import Path


_PROCESS_STARTED = time.perf_counter()


Expand Down
1 change: 0 additions & 1 deletion app_server/protocol/codec.py
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,6 @@
from app_server.errors import InvalidRequest, ParseError
from app_server.protocol.models import Request


DEFAULT_MAX_MESSAGE_BYTES = 1024 * 1024


Expand Down
1 change: 0 additions & 1 deletion app_server/protocol/models.py
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,6 @@
from dataclasses import dataclass, field
from typing import Any


RpcId = str | int | None


Expand Down
1 change: 0 additions & 1 deletion cli/automation_cli.py
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,6 @@
)
from core.domain.automation import AutomationActivationStatus, AutomationScheduleKind


ApplicationFactory = Callable[[], DeepCodeApplication]


Expand Down
1 change: 0 additions & 1 deletion cli/automation_foreground.py
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,6 @@
from core.domain.automation import AutomationRunStatus
from core.domain.event import DomainEvent


_EVENT_PAGE_SIZE = 500
_LIVE_WAKE_SECONDS = 0.25

Expand Down
3 changes: 1 addition & 2 deletions cli/loop_cli.py
Original file line number Diff line number Diff line change
Expand Up @@ -23,8 +23,6 @@

from rich.console import Console

from cli.tui import theme

from cli.execution_options import (
add_access_preset_argument,
add_reasoning_effort_argument,
Expand All @@ -37,6 +35,7 @@
resume_goal,
run_goal,
)
from cli.tui import theme
from cli.tui.renderer import EventRenderer
from core.application.errors import ApplicationError
from core.config import ConfigError
Expand Down
2 changes: 1 addition & 1 deletion cli/mcp_server.py
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,7 @@
import uuid
from typing import Any

import mcp.types as types
from mcp import types
from mcp.server.lowlevel import Server
from mcp.server.stdio import stdio_server

Expand Down
2 changes: 1 addition & 1 deletion cli/plugin_cli.py
Original file line number Diff line number Diff line change
Expand Up @@ -6,8 +6,8 @@
import json
import sys

from core.application.plugin_service import PluginDiscovery, PluginInfo, PluginService
from core.application.errors import ApplicationError
from core.application.plugin_service import PluginDiscovery, PluginInfo, PluginService
from core.plugins.host import LocalPluginHost
from core.skills.host import SkillWorkspaceRegistry

Expand Down
7 changes: 3 additions & 4 deletions cli/schedule_cli.py
Original file line number Diff line number Diff line change
Expand Up @@ -25,10 +25,9 @@

from rich.console import Console

from cli.tui import theme

from cli.goal_runner import GoalRunOptions, run_goal
from cli.execution_options import add_reasoning_effort_argument
from cli.goal_runner import GoalRunOptions, run_goal
from cli.tui import theme
from core.domain.thread_goal import ThreadGoalStatus
from core.loop.autodream import consolidate_memory
from core.schedule.keepalive import Continuation
Expand Down Expand Up @@ -63,7 +62,7 @@ async def task(run_index: int) -> RunOutcome:
return task


def _loop_task(args) -> "callable":
def _loop_task(args) -> callable:
async def task(run_index: int) -> RunOutcome:
workspace = os.path.abspath(args.workspace)
result = await run_goal(
Expand Down
4 changes: 2 additions & 2 deletions cli/skill_cli.py
Original file line number Diff line number Diff line change
Expand Up @@ -7,10 +7,10 @@
import os
import sys

from core.skills.management import LocalSkillManager
from core.skills.models import MUTABLE_SKILL_SCOPES, SkillRecord, SkillScope
from core.plugins.host import LocalPluginHost
from core.skills.host import SkillWorkspaceRegistry
from core.skills.management import LocalSkillManager
from core.skills.models import MUTABLE_SKILL_SCOPES, SkillRecord, SkillScope


def _parser() -> argparse.ArgumentParser:
Expand Down
4 changes: 2 additions & 2 deletions cli/transcript.py
Original file line number Diff line number Diff line change
Expand Up @@ -10,12 +10,12 @@ class TranscriptMode(StrEnum):
VERBOSE = "verbose"
SUMMARY = "summary"

def next(self) -> "TranscriptMode":
def next(self) -> TranscriptMode:
modes = tuple(type(self))
return modes[(modes.index(self) + 1) % len(modes)]

@classmethod
def parse(cls, value: str) -> "TranscriptMode":
def parse(cls, value: str) -> TranscriptMode:
clean = value.strip().lower()
try:
return cls(clean)
Expand Down
3 changes: 1 addition & 2 deletions cli/tui/app.py
Original file line number Diff line number Diff line change
Expand Up @@ -60,7 +60,6 @@
from core.file_lock import FileLease
from core.providers.reasoning import normalize_reasoning_effort


_MODEL_CATALOG_PREVIEW = 4 # models shown per connection in /model
# Sentinel: switch_model keeps the session's effort unless told otherwise.
_KEEP_EFFORT: object = object()
Expand Down Expand Up @@ -313,7 +312,7 @@ def model_overview(self) -> str:
else:
catalog = "no catalog configured — any model id accepted"
marker = " · current" if view.get("id") == profile.connection_id else ""
lines.append(f" {str(view.get('id', '')):<{width}} {catalog}{marker}")
lines.append(f" {view.get('id', '')!s:<{width}} {catalog}{marker}")
return "\n".join(lines)

def connection_model_catalog(self) -> list[tuple[str, list[dict]]]:
Expand Down
1 change: 0 additions & 1 deletion cli/tui/domain_events.py
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,6 @@
from core.application.event_service import DeliveryBatch, EventService
from core.domain.event import DomainEvent


_REPLAY_PAGE_SIZE = 500


Expand Down
1 change: 0 additions & 1 deletion cli/tui/renderer.py
Original file line number Diff line number Diff line change
Expand Up @@ -41,7 +41,6 @@
from core.events.protocol import Event
from core.reasoning import ReasoningAvailability, ReasoningChannel


_NORMAL_PREVIEW_CHARS = 240
_STATUS_DETAIL_CHARS = 72
_SUBJECT_CELLS = 88 # ceiling; the real budget is the terminal's width
Expand Down
3 changes: 2 additions & 1 deletion core/agent_runtime/compaction.py
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,8 @@

from __future__ import annotations

from typing import Any, Mapping, Protocol
from collections.abc import Mapping
from typing import Any, Protocol

from core.agent_runtime.helpers import find_legal_message_start

Expand Down
5 changes: 3 additions & 2 deletions core/agent_runtime/context.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,10 +3,11 @@
from __future__ import annotations

import os
from collections.abc import Mapping
from dataclasses import dataclass
from datetime import datetime
from pathlib import Path
from typing import Any, Mapping
from typing import Any
from xml.sax.saxutils import escape


Expand Down Expand Up @@ -47,7 +48,7 @@ class EnvironmentContext:
timezone: str

@classmethod
def for_workspace(cls, workspace: str | Path) -> "EnvironmentContext":
def for_workspace(cls, workspace: str | Path) -> EnvironmentContext:
now = datetime.now().astimezone()
return cls(
cwd=str(Path(workspace).expanduser().resolve(strict=False)),
Expand Down
1 change: 0 additions & 1 deletion core/agent_runtime/goal_runtime.py
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,6 @@
from dataclasses import dataclass
from typing import Any, Protocol


GOAL_TOOL_NAMES = frozenset({"get_goal", "update_goal"})
_GOAL_CLOSURE_PROMPT = """\
Before ending this Goal-associated Turn, call get_goal and compare the latest
Expand Down
4 changes: 0 additions & 4 deletions core/agent_runtime/hook.py
Original file line number Diff line number Diff line change
Expand Up @@ -43,8 +43,6 @@ async def before_iteration(self, context: AgentHookContext) -> None:
async def before_model_request(self, context: AgentHookContext) -> None:
"""Observe a user-visible model request before provider I/O begins."""

pass

async def on_stream(self, context: AgentHookContext, delta: str) -> None:
pass

Expand All @@ -62,8 +60,6 @@ async def on_stream_end(self, context: AgentHookContext, *, resuming: bool) -> N
async def on_model_response(self, context: AgentHookContext) -> None:
"""Observe one completed provider response before tools can run."""

pass

async def before_execute_tools(self, context: AgentHookContext) -> None:
pass

Expand Down
2 changes: 1 addition & 1 deletion core/agent_runtime/processes.py
Original file line number Diff line number Diff line change
Expand Up @@ -36,7 +36,7 @@ async def terminate_process_tree(
return
try:
await asyncio.wait_for(process.wait(), timeout=grace_seconds)
except asyncio.TimeoutError:
except TimeoutError:
pass
try:
os.killpg(process.pid, signal.SIGKILL)
Expand Down
72 changes: 60 additions & 12 deletions core/agent_runtime/runner.py
Original file line number Diff line number Diff line change
Expand Up @@ -19,34 +19,36 @@

from loguru import logger

from core.agent_runtime.injections import (
GoalObjectiveUpdated,
SubagentMessage,
UserSteer,
runtime_input_to_provider_message,
)
from core.agent_runtime.compaction import (
COMPACT_TRIGGER_FRACTION as _COMPACT_TRIGGER_FRACTION,
)
from core.agent_runtime.compaction import (
DEFAULT_COMPACTION_STRATEGY,
SUMMARIZATION_PROMPT as _SUMMARIZATION_PROMPT,
CompactionStrategy,
)
from core.agent_runtime.compaction import (
SUMMARIZATION_PROMPT as _SUMMARIZATION_PROMPT,
)
from core.agent_runtime.helpers import (
build_assistant_message,
history_signature,
estimate_message_tokens,
find_legal_message_start,
history_signature,
maybe_persist_tool_result,
truncate_text,
)
from core.agent_runtime.token_meter import (
DEFAULT_TOKEN_METER_FACTORY,
TokenMeter,
)
from core.agent_runtime.hook import AgentHook, AgentHookContext
from core.agent_runtime.injections import (
GoalObjectiveUpdated,
SubagentMessage,
UserSteer,
runtime_input_to_provider_message,
)
from core.agent_runtime.pruner import ToolResultPruner
from core.agent_runtime.repeat_guard import (
DEFAULT_THRESHOLDS as DEFAULT_REPEAT_THRESHOLDS,
)
from core.agent_runtime.repeat_guard import (
RepeatCallTracker,
)
from core.agent_runtime.runtime import (
Expand All @@ -57,6 +59,10 @@
is_blank_text,
repeated_external_lookup_error,
)
from core.agent_runtime.token_meter import (
DEFAULT_TOKEN_METER_FACTORY,
TokenMeter,
)
from core.agent_runtime.tools.base import ToolResult
from core.agent_runtime.tools.registry import ToolRegistry
from core.providers.base import (
Expand Down Expand Up @@ -231,6 +237,13 @@ class AgentRunSpec:
# follow-up prompt and the loop keeps going. ``stop_hook_active`` is passed
# so a well-behaved hook stops blocking after its first continuation.
stop_hook: Any | None = None
# P1-5 (GenAI lesson 15): compaction-as-memory. Called with the handoff
# summary + anchor metadata (session key, phase, timestamp, replaced
# message count) after a compaction successfully shrinks the history, so
# the host can deposit the summary into the memory vault — compressed
# sessions stay retrievable instead of vanishing. Must never raise; a
# failing sink is logged and swallowed.
compaction_summary_sink: Any | None = None

def allowed_tool_names(self) -> frozenset[str] | None:
if self.tool_filter is None:
Expand Down Expand Up @@ -1801,6 +1814,7 @@ async def _maybe_compact(
budget,
_COMPACT_TRIGGER_FRACTION,
)
self._notify_compaction_summary(spec, summary, messages, compacted, "auto")
return compacted

def _estimate_prompt(
Expand Down Expand Up @@ -1902,8 +1916,42 @@ async def compact_history(
"Compaction would not shrink the conversation. "
"The conversation is unchanged."
)
self._notify_compaction_summary(spec, summary, messages, compacted, "manual")
return compacted, "compacted"

def _notify_compaction_summary(
self,
spec: AgentRunSpec,
summary: str,
before: list[dict[str, Any]],
after: list[dict[str, Any]],
phase: str,
) -> None:
"""Deposit the handoff summary + anchors into the memory sink (P1-5).

Pure fire-and-forget: a failing or absent sink never affects the
compaction result. Anchors keep the summary retrievable and
attributable (lesson 15: compressed summaries must carry session id,
phase, and timestamps rather than vanishing into the vault).
"""
if spec.compaction_summary_sink is None:
return
import time as _time

anchor = {
"session_key": spec.session_key or "default",
"phase": phase,
"at": _time.strftime("%Y-%m-%dT%H:%M:%S"),
"messages_before": len(before),
"messages_after": len(after),
"chars_before": self._history_chars(before),
"chars_after": self._history_chars(after),
}
try:
spec.compaction_summary_sink(summary, anchor)
except Exception: # noqa: BLE001 - memory work must never break the turn
logger.debug("compaction summary sink failed", exc_info=True)

def _overflow_reduce(
self,
spec: AgentRunSpec,
Expand Down
5 changes: 4 additions & 1 deletion core/agent_runtime/token_meter.py
Original file line number Diff line number Diff line change
Expand Up @@ -28,12 +28,15 @@

from __future__ import annotations

from collections.abc import Mapping
from dataclasses import dataclass
from typing import Any, Mapping, Protocol
from typing import Any, Protocol

from core.agent_runtime.helpers import (
estimate_message_tokens,
estimate_prompt_tokens_chain,
)
from core.agent_runtime.helpers import (
history_signature as _shape,
)

Expand Down
2 changes: 1 addition & 1 deletion core/agent_runtime/tools/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -15,8 +15,8 @@
"MCPToolWrapper",
"Schema",
"Tool",
"ToolResult",
"ToolRegistry",
"ToolResult",
"connect_mcp_servers",
"tool_parameters",
]
Loading
Loading