codex-agent-framework 0.1.27__tar.gz → 0.1.28__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- {codex_agent_framework-0.1.27 → codex_agent_framework-0.1.28}/CHANGELOG.md +17 -0
- {codex_agent_framework-0.1.27/codex_agent_framework.egg-info → codex_agent_framework-0.1.28}/PKG-INFO +1 -1
- {codex_agent_framework-0.1.27 → codex_agent_framework-0.1.28}/codex_agent/__init__.py +2 -1
- {codex_agent_framework-0.1.27 → codex_agent_framework-0.1.28}/codex_agent/agent.py +39 -29
- {codex_agent_framework-0.1.27 → codex_agent_framework-0.1.28}/codex_agent/agent_runtime.py +152 -5
- {codex_agent_framework-0.1.27 → codex_agent_framework-0.1.28}/codex_agent/context.py +43 -3
- {codex_agent_framework-0.1.27 → codex_agent_framework-0.1.28}/codex_agent/event.py +4 -0
- {codex_agent_framework-0.1.27 → codex_agent_framework-0.1.28}/codex_agent/server/core.py +5 -1
- {codex_agent_framework-0.1.27 → codex_agent_framework-0.1.28}/codex_agent/status.py +51 -38
- {codex_agent_framework-0.1.27 → codex_agent_framework-0.1.28}/codex_agent/stream.py +40 -0
- {codex_agent_framework-0.1.27 → codex_agent_framework-0.1.28}/codex_agent/tui/chat.py +262 -27
- codex_agent_framework-0.1.28/codex_agent/version.py +1 -0
- {codex_agent_framework-0.1.27 → codex_agent_framework-0.1.28/codex_agent_framework.egg-info}/PKG-INFO +1 -1
- {codex_agent_framework-0.1.27 → codex_agent_framework-0.1.28}/pyproject.toml +1 -1
- {codex_agent_framework-0.1.27 → codex_agent_framework-0.1.28}/tests/test_agent.py +38 -0
- {codex_agent_framework-0.1.27 → codex_agent_framework-0.1.28}/tests/test_chat.py +158 -25
- {codex_agent_framework-0.1.27 → codex_agent_framework-0.1.28}/tests/test_server.py +169 -0
- codex_agent_framework-0.1.27/codex_agent/version.py +0 -1
- {codex_agent_framework-0.1.27 → codex_agent_framework-0.1.28}/LICENSE +0 -0
- {codex_agent_framework-0.1.27 → codex_agent_framework-0.1.28}/MANIFEST.in +0 -0
- {codex_agent_framework-0.1.27 → codex_agent_framework-0.1.28}/README.md +0 -0
- {codex_agent_framework-0.1.27 → codex_agent_framework-0.1.28}/codex_agent/__main__.py +0 -0
- {codex_agent_framework-0.1.27 → codex_agent_framework-0.1.28}/codex_agent/ai.py +0 -0
- {codex_agent_framework-0.1.27 → codex_agent_framework-0.1.28}/codex_agent/browser.py +0 -0
- {codex_agent_framework-0.1.27 → codex_agent_framework-0.1.28}/codex_agent/builtin_commands.py +0 -0
- {codex_agent_framework-0.1.27 → codex_agent_framework-0.1.28}/codex_agent/builtin_plugins/__init__.py +0 -0
- {codex_agent_framework-0.1.27 → codex_agent_framework-0.1.28}/codex_agent/builtin_plugins/bash/__init__.py +0 -0
- {codex_agent_framework-0.1.27 → codex_agent_framework-0.1.28}/codex_agent/builtin_plugins/bash/requirements.txt +0 -0
- {codex_agent_framework-0.1.27 → codex_agent_framework-0.1.28}/codex_agent/builtin_plugins/browser/__init__.py +0 -0
- {codex_agent_framework-0.1.27 → codex_agent_framework-0.1.28}/codex_agent/builtin_plugins/browser/requirements.txt +0 -0
- {codex_agent_framework-0.1.27 → codex_agent_framework-0.1.28}/codex_agent/builtin_plugins/content/__init__.py +0 -0
- {codex_agent_framework-0.1.27 → codex_agent_framework-0.1.28}/codex_agent/builtin_plugins/content/requirements.txt +0 -0
- {codex_agent_framework-0.1.27 → codex_agent_framework-0.1.28}/codex_agent/builtin_plugins/content/system.py +0 -0
- {codex_agent_framework-0.1.27 → codex_agent_framework-0.1.28}/codex_agent/builtin_plugins/content/vision.py +0 -0
- {codex_agent_framework-0.1.27 → codex_agent_framework-0.1.28}/codex_agent/builtin_plugins/context/__init__.py +0 -0
- {codex_agent_framework-0.1.27 → codex_agent_framework-0.1.28}/codex_agent/builtin_plugins/desktop/__init__.py +0 -0
- {codex_agent_framework-0.1.27 → codex_agent_framework-0.1.28}/codex_agent/builtin_plugins/desktop/requirements.txt +0 -0
- {codex_agent_framework-0.1.27 → codex_agent_framework-0.1.28}/codex_agent/builtin_plugins/environment/__init__.py +0 -0
- {codex_agent_framework-0.1.27 → codex_agent_framework-0.1.28}/codex_agent/builtin_plugins/environment/requirements.txt +0 -0
- {codex_agent_framework-0.1.27 → codex_agent_framework-0.1.28}/codex_agent/builtin_plugins/files/__init__.py +0 -0
- {codex_agent_framework-0.1.27 → codex_agent_framework-0.1.28}/codex_agent/builtin_plugins/files/requirements.txt +0 -0
- {codex_agent_framework-0.1.27 → codex_agent_framework-0.1.28}/codex_agent/builtin_plugins/files/tools.py +0 -0
- {codex_agent_framework-0.1.27 → codex_agent_framework-0.1.28}/codex_agent/builtin_plugins/image_generation/__init__.py +0 -0
- {codex_agent_framework-0.1.27 → codex_agent_framework-0.1.28}/codex_agent/builtin_plugins/image_generation/image_generation_system_prompt.txt +0 -0
- {codex_agent_framework-0.1.27 → codex_agent_framework-0.1.28}/codex_agent/builtin_plugins/image_generation/requirements.txt +0 -0
- {codex_agent_framework-0.1.27 → codex_agent_framework-0.1.28}/codex_agent/builtin_plugins/interface/__init__.py +0 -0
- {codex_agent_framework-0.1.27 → codex_agent_framework-0.1.28}/codex_agent/builtin_plugins/interface/requirements.txt +0 -0
- {codex_agent_framework-0.1.27 → codex_agent_framework-0.1.28}/codex_agent/builtin_plugins/memory/__init__.py +0 -0
- {codex_agent_framework-0.1.27 → codex_agent_framework-0.1.28}/codex_agent/builtin_plugins/memory/requirements.txt +0 -0
- {codex_agent_framework-0.1.27 → codex_agent_framework-0.1.28}/codex_agent/builtin_plugins/planner/__init__.py +0 -0
- {codex_agent_framework-0.1.27 → codex_agent_framework-0.1.28}/codex_agent/builtin_plugins/planner/requirements.txt +0 -0
- {codex_agent_framework-0.1.27 → codex_agent_framework-0.1.28}/codex_agent/builtin_plugins/python/__init__.py +0 -0
- {codex_agent_framework-0.1.27 → codex_agent_framework-0.1.28}/codex_agent/builtin_plugins/python/requirements.txt +0 -0
- {codex_agent_framework-0.1.27 → codex_agent_framework-0.1.28}/codex_agent/builtin_plugins/realtime/__init__.py +0 -0
- {codex_agent_framework-0.1.27 → codex_agent_framework-0.1.28}/codex_agent/builtin_plugins/realtime/audio.py +0 -0
- {codex_agent_framework-0.1.27 → codex_agent_framework-0.1.28}/codex_agent/builtin_plugins/realtime/bridge.py +0 -0
- {codex_agent_framework-0.1.27 → codex_agent_framework-0.1.28}/codex_agent/builtin_plugins/realtime/manager.py +0 -0
- {codex_agent_framework-0.1.27 → codex_agent_framework-0.1.28}/codex_agent/builtin_plugins/realtime/requirements.txt +0 -0
- {codex_agent_framework-0.1.27 → codex_agent_framework-0.1.28}/codex_agent/builtin_plugins/realtime/session.py +0 -0
- {codex_agent_framework-0.1.27 → codex_agent_framework-0.1.28}/codex_agent/builtin_plugins/scheduler/__init__.py +0 -0
- {codex_agent_framework-0.1.27 → codex_agent_framework-0.1.28}/codex_agent/builtin_plugins/scheduler/requirements.txt +0 -0
- {codex_agent_framework-0.1.27 → codex_agent_framework-0.1.28}/codex_agent/builtin_plugins/subagents/__init__.py +0 -0
- {codex_agent_framework-0.1.27 → codex_agent_framework-0.1.28}/codex_agent/builtin_plugins/subagents/profiles/explorer.json +0 -0
- {codex_agent_framework-0.1.27 → codex_agent_framework-0.1.28}/codex_agent/builtin_plugins/subagents/requirements.txt +0 -0
- {codex_agent_framework-0.1.27 → codex_agent_framework-0.1.28}/codex_agent/builtin_plugins/tts/__init__.py +0 -0
- {codex_agent_framework-0.1.27 → codex_agent_framework-0.1.28}/codex_agent/builtin_plugins/tts/requirements.txt +0 -0
- {codex_agent_framework-0.1.27 → codex_agent_framework-0.1.28}/codex_agent/cli/__init__.py +0 -0
- {codex_agent_framework-0.1.27 → codex_agent_framework-0.1.28}/codex_agent/cli/headless.py +0 -0
- {codex_agent_framework-0.1.27 → codex_agent_framework-0.1.28}/codex_agent/cli/main.py +0 -0
- {codex_agent_framework-0.1.27 → codex_agent_framework-0.1.28}/codex_agent/cli/output.py +0 -0
- {codex_agent_framework-0.1.27 → codex_agent_framework-0.1.28}/codex_agent/cli/root.py +0 -0
- {codex_agent_framework-0.1.27 → codex_agent_framework-0.1.28}/codex_agent/cli/runner.py +0 -0
- {codex_agent_framework-0.1.27 → codex_agent_framework-0.1.28}/codex_agent/client.py +0 -0
- {codex_agent_framework-0.1.27 → codex_agent_framework-0.1.28}/codex_agent/command.py +0 -0
- {codex_agent_framework-0.1.27 → codex_agent_framework-0.1.28}/codex_agent/config.py +0 -0
- {codex_agent_framework-0.1.27 → codex_agent_framework-0.1.28}/codex_agent/desktop.py +0 -0
- {codex_agent_framework-0.1.27 → codex_agent_framework-0.1.28}/codex_agent/get_text/__init__.py +0 -0
- {codex_agent_framework-0.1.27 → codex_agent_framework-0.1.28}/codex_agent/get_text/default_gitignore +0 -0
- {codex_agent_framework-0.1.27 → codex_agent_framework-0.1.28}/codex_agent/get_text/get_text.py +0 -0
- {codex_agent_framework-0.1.27 → codex_agent_framework-0.1.28}/codex_agent/get_text/simpler_get_text.py +0 -0
- {codex_agent_framework-0.1.27 → codex_agent_framework-0.1.28}/codex_agent/hooks.py +0 -0
- {codex_agent_framework-0.1.27 → codex_agent_framework-0.1.28}/codex_agent/image.py +0 -0
- {codex_agent_framework-0.1.27 → codex_agent_framework-0.1.28}/codex_agent/latex.py +0 -0
- {codex_agent_framework-0.1.27 → codex_agent_framework-0.1.28}/codex_agent/mainloop.py +0 -0
- {codex_agent_framework-0.1.27 → codex_agent_framework-0.1.28}/codex_agent/message.py +0 -0
- {codex_agent_framework-0.1.27 → codex_agent_framework-0.1.28}/codex_agent/plugin.py +0 -0
- {codex_agent_framework-0.1.27 → codex_agent_framework-0.1.28}/codex_agent/prompts/system_prompt.txt +0 -0
- {codex_agent_framework-0.1.27 → codex_agent_framework-0.1.28}/codex_agent/provider.py +0 -0
- {codex_agent_framework-0.1.27 → codex_agent_framework-0.1.28}/codex_agent/runtime.py +0 -0
- {codex_agent_framework-0.1.27 → codex_agent_framework-0.1.28}/codex_agent/scheduler.py +0 -0
- {codex_agent_framework-0.1.27 → codex_agent_framework-0.1.28}/codex_agent/scripts/install-system-dependencies.sh +0 -0
- {codex_agent_framework-0.1.27 → codex_agent_framework-0.1.28}/codex_agent/server/__init__.py +0 -0
- {codex_agent_framework-0.1.27 → codex_agent_framework-0.1.28}/codex_agent/server/app.py +0 -0
- {codex_agent_framework-0.1.27 → codex_agent_framework-0.1.28}/codex_agent/service.py +0 -0
- {codex_agent_framework-0.1.27 → codex_agent_framework-0.1.28}/codex_agent/sessions.py +0 -0
- {codex_agent_framework-0.1.27 → codex_agent_framework-0.1.28}/codex_agent/stream_utils.py +0 -0
- {codex_agent_framework-0.1.27 → codex_agent_framework-0.1.28}/codex_agent/tool.py +0 -0
- {codex_agent_framework-0.1.27 → codex_agent_framework-0.1.28}/codex_agent/tray.py +0 -0
- {codex_agent_framework-0.1.27 → codex_agent_framework-0.1.28}/codex_agent/tui/__init__.py +0 -0
- {codex_agent_framework-0.1.27 → codex_agent_framework-0.1.28}/codex_agent/tui/lifecycle.py +0 -0
- {codex_agent_framework-0.1.27 → codex_agent_framework-0.1.28}/codex_agent/utils.py +0 -0
- {codex_agent_framework-0.1.27 → codex_agent_framework-0.1.28}/codex_agent/worker.py +0 -0
- {codex_agent_framework-0.1.27 → codex_agent_framework-0.1.28}/codex_agent_framework.egg-info/SOURCES.txt +0 -0
- {codex_agent_framework-0.1.27 → codex_agent_framework-0.1.28}/codex_agent_framework.egg-info/dependency_links.txt +0 -0
- {codex_agent_framework-0.1.27 → codex_agent_framework-0.1.28}/codex_agent_framework.egg-info/entry_points.txt +0 -0
- {codex_agent_framework-0.1.27 → codex_agent_framework-0.1.28}/codex_agent_framework.egg-info/requires.txt +0 -0
- {codex_agent_framework-0.1.27 → codex_agent_framework-0.1.28}/codex_agent_framework.egg-info/top_level.txt +0 -0
- {codex_agent_framework-0.1.27 → codex_agent_framework-0.1.28}/dependencies.txt +0 -0
- {codex_agent_framework-0.1.27 → codex_agent_framework-0.1.28}/setup.cfg +0 -0
- {codex_agent_framework-0.1.27 → codex_agent_framework-0.1.28}/tests/test_agent_runtime.py +0 -0
- {codex_agent_framework-0.1.27 → codex_agent_framework-0.1.28}/tests/test_ai.py +0 -0
- {codex_agent_framework-0.1.27 → codex_agent_framework-0.1.28}/tests/test_browser.py +0 -0
- {codex_agent_framework-0.1.27 → codex_agent_framework-0.1.28}/tests/test_builtin_config.py +0 -0
- {codex_agent_framework-0.1.27 → codex_agent_framework-0.1.28}/tests/test_cli.py +0 -0
- {codex_agent_framework-0.1.27 → codex_agent_framework-0.1.28}/tests/test_cli_headless.py +0 -0
- {codex_agent_framework-0.1.27 → codex_agent_framework-0.1.28}/tests/test_cli_root.py +0 -0
- {codex_agent_framework-0.1.27 → codex_agent_framework-0.1.28}/tests/test_client.py +0 -0
- {codex_agent_framework-0.1.27 → codex_agent_framework-0.1.28}/tests/test_events.py +0 -0
- {codex_agent_framework-0.1.27 → codex_agent_framework-0.1.28}/tests/test_get_text_browser.py +0 -0
- {codex_agent_framework-0.1.27 → codex_agent_framework-0.1.28}/tests/test_hooks.py +0 -0
- {codex_agent_framework-0.1.27 → codex_agent_framework-0.1.28}/tests/test_image_message.py +0 -0
- {codex_agent_framework-0.1.27 → codex_agent_framework-0.1.28}/tests/test_local_desktop.py +0 -0
- {codex_agent_framework-0.1.27 → codex_agent_framework-0.1.28}/tests/test_memory.py +0 -0
- {codex_agent_framework-0.1.27 → codex_agent_framework-0.1.28}/tests/test_messages.py +0 -0
- {codex_agent_framework-0.1.27 → codex_agent_framework-0.1.28}/tests/test_planner.py +0 -0
- {codex_agent_framework-0.1.27 → codex_agent_framework-0.1.28}/tests/test_scheduler.py +0 -0
- {codex_agent_framework-0.1.27 → codex_agent_framework-0.1.28}/tests/test_service.py +0 -0
- {codex_agent_framework-0.1.27 → codex_agent_framework-0.1.28}/tests/test_tray.py +0 -0
- {codex_agent_framework-0.1.27 → codex_agent_framework-0.1.28}/tests/test_tui.py +0 -0
- {codex_agent_framework-0.1.27 → codex_agent_framework-0.1.28}/tests/test_utils.py +0 -0
- {codex_agent_framework-0.1.27 → codex_agent_framework-0.1.28}/tests/test_worker.py +0 -0
|
@@ -4,6 +4,23 @@ All notable changes to this project will be documented in this file.
|
|
|
4
4
|
|
|
5
5
|
This project loosely follows [Keep a Changelog](https://keepachangelog.com/en/1.1.0/) and uses semantic versioning where practical.
|
|
6
6
|
|
|
7
|
+
## [0.1.28] - 2026-05-15
|
|
8
|
+
### Added
|
|
9
|
+
- Add `StreamManager` as a dedicated stream-processing manager and expose it through the package API.
|
|
10
|
+
|
|
11
|
+
### Changed
|
|
12
|
+
- Improve Textual TUI streaming performance with coalesced replay/live deltas and safer incremental rendering.
|
|
13
|
+
- Make terminal resize handling re-render the TUI transcript and status cleanly.
|
|
14
|
+
- Treat the worker status cache as the single status source of truth, with process fallback limited to bootstrap.
|
|
15
|
+
|
|
16
|
+
### Fixed
|
|
17
|
+
- Keep `/status`, context providers, and the TUI status bar aligned after worker restarts and busy turns.
|
|
18
|
+
- Preserve wrapper/message partition counters and token splits across context builds, API status updates, and restarts.
|
|
19
|
+
- Avoid status update events confusing stale-turn detection.
|
|
20
|
+
|
|
21
|
+
### Tests
|
|
22
|
+
- Expand regression coverage for status cache replication, fallback bootstrap behavior, wrapper partitions, TUI delta batching, replay coalescing, and resize handling; the current suite validates at 576 passed.
|
|
23
|
+
|
|
7
24
|
## [0.1.25] - 2026-05-12
|
|
8
25
|
### Added
|
|
9
26
|
- Add bundled plugin `requirements.txt` metadata for built-in capabilities and move image generation, realtime voice, and TTS into plugin-scoped packages.
|
|
@@ -60,7 +60,7 @@ from .image import ImageMessage
|
|
|
60
60
|
from .mainloop import MainLoopManager
|
|
61
61
|
from .plugin import Plugin, PluginsManager, event_handler, hook_handler, stream_processor
|
|
62
62
|
from .command import AgentCommand, AgentFuture, AgentTurn, CommandManager, command
|
|
63
|
-
from .stream import AgentStream
|
|
63
|
+
from .stream import AgentStream, StreamManager
|
|
64
64
|
from .provider import ProvidersManager, provider
|
|
65
65
|
from .runtime import RuntimeManager
|
|
66
66
|
from .sessions import AgentSession, SessionsManager
|
|
@@ -138,6 +138,7 @@ __all__ = [
|
|
|
138
138
|
"AgentFuture",
|
|
139
139
|
"AgentTurn",
|
|
140
140
|
"AgentStream",
|
|
141
|
+
"StreamManager",
|
|
141
142
|
"CommandManager",
|
|
142
143
|
"command",
|
|
143
144
|
"provider",
|
|
@@ -14,6 +14,7 @@ from .runtime import RuntimeManager
|
|
|
14
14
|
from .scheduler import AgentScheduler
|
|
15
15
|
from .sessions import SessionsManager
|
|
16
16
|
from .status import StatusManager
|
|
17
|
+
from .stream import StreamManager
|
|
17
18
|
from .tool import ToolsManager, reset_current_agent, set_current_agent
|
|
18
19
|
from .ai import AIClient
|
|
19
20
|
from .event import AssistantInterruptRequestedEvent, AssistantInterrupted, AssistantInterruptedEvent, EventBus
|
|
@@ -32,26 +33,20 @@ class Agent:
|
|
|
32
33
|
ensure_runtime_dir()
|
|
33
34
|
self.init_execution_environment()
|
|
34
35
|
self.config_manager=ConfigManager(self)
|
|
35
|
-
self.config=self.config_manager.config
|
|
36
36
|
self.config_manager.load()
|
|
37
37
|
self.config_manager.update(kwargs, save=False)
|
|
38
38
|
self.config_manager.apply_openai_capability_defaults()
|
|
39
39
|
self.events=EventBus()
|
|
40
40
|
self.tools_manager=ToolsManager(self)
|
|
41
|
-
self.tools=self.tools_manager.tools
|
|
42
41
|
self.providers_manager=ProvidersManager(self)
|
|
43
|
-
self.providers=self.providers_manager.providers
|
|
44
42
|
self.command_manager=CommandManager(self)
|
|
45
|
-
self.commands=self.command_manager.commands
|
|
46
43
|
self.context_manager=ContextManager(self)
|
|
47
44
|
self.plugins_manager=PluginsManager(self)
|
|
48
|
-
self.plugins=self.plugins_manager.plugins
|
|
49
45
|
self.sessions_manager=SessionsManager(self)
|
|
50
46
|
self.runtime=RuntimeManager(self)
|
|
51
47
|
self.scheduler=AgentScheduler(self)
|
|
52
48
|
self.hooks=HookManager(self)
|
|
53
|
-
self.
|
|
54
|
-
self.stream_processor_owners={}
|
|
49
|
+
self.stream_manager=StreamManager(self)
|
|
55
50
|
self.mainloop_manager=MainLoopManager(self)
|
|
56
51
|
self._interrupt_requested=ThreadEvent()
|
|
57
52
|
self.current_session_id=None
|
|
@@ -86,6 +81,40 @@ class Agent:
|
|
|
86
81
|
mainloop_manager.record_event(event)
|
|
87
82
|
return event
|
|
88
83
|
|
|
84
|
+
@property
|
|
85
|
+
def config(self):
|
|
86
|
+
return self.config_manager.config
|
|
87
|
+
|
|
88
|
+
@config.setter
|
|
89
|
+
def config(self, value):
|
|
90
|
+
self.config_manager.config = value
|
|
91
|
+
if hasattr(self.config_manager, "configs"):
|
|
92
|
+
self.config_manager.configs.agent = value
|
|
93
|
+
|
|
94
|
+
@property
|
|
95
|
+
def tools(self):
|
|
96
|
+
return self.tools_manager.tools
|
|
97
|
+
|
|
98
|
+
@property
|
|
99
|
+
def providers(self):
|
|
100
|
+
return self.providers_manager.providers
|
|
101
|
+
|
|
102
|
+
@property
|
|
103
|
+
def commands(self):
|
|
104
|
+
return self.command_manager.commands
|
|
105
|
+
|
|
106
|
+
@property
|
|
107
|
+
def plugins(self):
|
|
108
|
+
return self.plugins_manager.plugins
|
|
109
|
+
|
|
110
|
+
@property
|
|
111
|
+
def stream_processors(self):
|
|
112
|
+
return self.stream_manager.processors
|
|
113
|
+
|
|
114
|
+
@property
|
|
115
|
+
def stream_processor_owners(self):
|
|
116
|
+
return self.stream_manager.owners
|
|
117
|
+
|
|
89
118
|
@property
|
|
90
119
|
def busy(self):
|
|
91
120
|
return self.mainloop_manager.busy
|
|
@@ -274,32 +303,13 @@ class Agent:
|
|
|
274
303
|
return self.plugins_manager.reload_plugin(plugin_or_name, timeout=timeout)
|
|
275
304
|
|
|
276
305
|
def add_stream_processor(self, key, processor, owner=None):
|
|
277
|
-
|
|
278
|
-
processors.append(processor)
|
|
279
|
-
if owner is not None:
|
|
280
|
-
self.stream_processor_owners.setdefault(str(key), {})[processor] = owner
|
|
281
|
-
return processor
|
|
306
|
+
return self.stream_manager.add(key, processor, owner=owner)
|
|
282
307
|
|
|
283
308
|
def get_stream_processors(self, key):
|
|
284
|
-
return
|
|
309
|
+
return self.stream_manager.get(key)
|
|
285
310
|
|
|
286
311
|
def remove_stream_processors_by_owner(self, owner):
|
|
287
|
-
|
|
288
|
-
for key, processors in list(self.stream_processors.items()):
|
|
289
|
-
owners = self.stream_processor_owners.get(key, {})
|
|
290
|
-
kept = []
|
|
291
|
-
for processor in processors:
|
|
292
|
-
if owners.get(processor) == owner:
|
|
293
|
-
removed.setdefault(key, []).append(processor)
|
|
294
|
-
owners.pop(processor, None)
|
|
295
|
-
else:
|
|
296
|
-
kept.append(processor)
|
|
297
|
-
if kept:
|
|
298
|
-
self.stream_processors[key] = kept
|
|
299
|
-
else:
|
|
300
|
-
self.stream_processors.pop(key, None)
|
|
301
|
-
self.stream_processor_owners.pop(key, None)
|
|
302
|
-
return removed
|
|
312
|
+
return self.stream_manager.remove_by_owner(owner)
|
|
303
313
|
|
|
304
314
|
def add_hook(self, key, func=None, owner=None):
|
|
305
315
|
"""Register a runtime hook.
|
|
@@ -8,8 +8,11 @@ from abc import ABC, abstractmethod
|
|
|
8
8
|
from modict import modict
|
|
9
9
|
|
|
10
10
|
from .agent import Agent
|
|
11
|
+
from .config import AgentConfig
|
|
11
12
|
from .event import Event
|
|
12
|
-
from .message import UserMessage, message_from_data
|
|
13
|
+
from .message import ToolResultWrapper, UserMessage, message_from_data
|
|
14
|
+
from .sessions import AgentSession
|
|
15
|
+
from .utils import token_count, truncate
|
|
13
16
|
|
|
14
17
|
|
|
15
18
|
def jsonable(value):
|
|
@@ -270,13 +273,38 @@ class ProcessAgentRuntime(AgentRuntime):
|
|
|
270
273
|
current_turn_id=None,
|
|
271
274
|
current_session_id=None,
|
|
272
275
|
status=modict(),
|
|
273
|
-
config=
|
|
276
|
+
config=AgentConfig(),
|
|
274
277
|
session=modict(),
|
|
275
278
|
sessions=modict(current=None, sessions=[]),
|
|
276
279
|
messages=[],
|
|
277
280
|
tools=modict(),
|
|
278
281
|
wakeups=[],
|
|
279
282
|
)
|
|
283
|
+
self.hydrate_local_session_cache()
|
|
284
|
+
|
|
285
|
+
def local_session_id(self):
|
|
286
|
+
session_id = self.agent_kwargs.get("session")
|
|
287
|
+
if not session_id or session_id == "latest":
|
|
288
|
+
try:
|
|
289
|
+
sessions = AgentSession.list_sessions()
|
|
290
|
+
except Exception:
|
|
291
|
+
return None
|
|
292
|
+
return sessions[0] if sessions else None
|
|
293
|
+
return session_id if AgentSession.can_load(session_id) else None
|
|
294
|
+
|
|
295
|
+
def hydrate_local_session_cache(self):
|
|
296
|
+
session_id = self.local_session_id()
|
|
297
|
+
if not session_id:
|
|
298
|
+
return False
|
|
299
|
+
try:
|
|
300
|
+
session = AgentSession.load(session_id)
|
|
301
|
+
except Exception:
|
|
302
|
+
return False
|
|
303
|
+
self.cache.current_session_id = session.session_id
|
|
304
|
+
self.cache.session = session
|
|
305
|
+
self.cache.messages = list(session.messages)
|
|
306
|
+
self.refresh_cached_wrapper_status(hydrate=False)
|
|
307
|
+
return True
|
|
280
308
|
|
|
281
309
|
def start(self):
|
|
282
310
|
with self.start_lock:
|
|
@@ -293,6 +321,7 @@ class ProcessAgentRuntime(AgentRuntime):
|
|
|
293
321
|
self.process.start()
|
|
294
322
|
self.event_thread = threading.Thread(target=self._event_loop, name="agent-runtime-events", daemon=True)
|
|
295
323
|
self.event_thread.start()
|
|
324
|
+
self.hydrate_local_session_cache()
|
|
296
325
|
try:
|
|
297
326
|
self.call("start", timeout=self.command_timeout, autostart=False)
|
|
298
327
|
self.refresh_cache()
|
|
@@ -339,6 +368,8 @@ class ProcessAgentRuntime(AgentRuntime):
|
|
|
339
368
|
except Exception:
|
|
340
369
|
pass
|
|
341
370
|
self.update_status_cache(self.cache.get("status") or {})
|
|
371
|
+
if not self.cache.get("status"):
|
|
372
|
+
self.refresh_cached_wrapper_status()
|
|
342
373
|
return self.cache
|
|
343
374
|
|
|
344
375
|
def call(self, name, payload=None, timeout=None, autostart=True):
|
|
@@ -391,6 +422,107 @@ class ProcessAgentRuntime(AgentRuntime):
|
|
|
391
422
|
self.cache.current_session_id = status.get("session_id", self.cache.current_session_id)
|
|
392
423
|
return status
|
|
393
424
|
|
|
425
|
+
def cached_message_from_event(self, message):
|
|
426
|
+
if not message:
|
|
427
|
+
return None
|
|
428
|
+
try:
|
|
429
|
+
return message_from_data(message)
|
|
430
|
+
except Exception:
|
|
431
|
+
return modict(message)
|
|
432
|
+
|
|
433
|
+
def is_cached_wrapper(self, message):
|
|
434
|
+
if isinstance(message, ToolResultWrapper):
|
|
435
|
+
return True
|
|
436
|
+
types = message.get("types") or []
|
|
437
|
+
return message.get("type") == "tool_result_wrapper" or "tool_result_wrapper" in types
|
|
438
|
+
|
|
439
|
+
def cached_wrapper_tokens(self, messages):
|
|
440
|
+
total = 0
|
|
441
|
+
config = modict(self.cache.get("config") or {})
|
|
442
|
+
max_tokens = int(config.get("max_input_tokens", 8000) or 8000)
|
|
443
|
+
model = config.get("model")
|
|
444
|
+
for message in messages:
|
|
445
|
+
content = message.get("content")
|
|
446
|
+
if not isinstance(content, str) or not content:
|
|
447
|
+
continue
|
|
448
|
+
total += token_count(truncate(content, max_tokens=max_tokens, model=model), model=model)
|
|
449
|
+
return total
|
|
450
|
+
|
|
451
|
+
def refresh_cached_wrapper_status(self, hydrate=True):
|
|
452
|
+
status = modict(self.cache.status or {})
|
|
453
|
+
if hydrate and not self.cache.get("messages"):
|
|
454
|
+
self.hydrate_local_session_cache()
|
|
455
|
+
messages = list(self.cache.get("messages") or [])
|
|
456
|
+
wrappers = [message for message in messages if self.is_cached_wrapper(message)]
|
|
457
|
+
active = [
|
|
458
|
+
message for message in wrappers
|
|
459
|
+
if isinstance(message.get("turns_left", -1), int) and message.get("turns_left", -1) > 0
|
|
460
|
+
]
|
|
461
|
+
vanished = [message for message in wrappers if message.get("turns_left", -1) == 0]
|
|
462
|
+
active_tokens = self.cached_wrapper_tokens(active)
|
|
463
|
+
vanished_tokens = self.cached_wrapper_tokens(vanished)
|
|
464
|
+
visible_non_wrappers = [
|
|
465
|
+
message for message in messages
|
|
466
|
+
if not self.is_cached_wrapper(message) and message.get("turns_left", -1) != 0
|
|
467
|
+
]
|
|
468
|
+
estimated_base_tokens = self.cached_wrapper_tokens(visible_non_wrappers)
|
|
469
|
+
# The fallback cache is updated by message events while the worker may be busy. Do not preserve
|
|
470
|
+
# a stale worker-provided context_current_tokens here: otherwise wrapper/message counts move but
|
|
471
|
+
# the context percentage appears frozen until a full worker status succeeds.
|
|
472
|
+
current_tokens = estimated_base_tokens + active_tokens
|
|
473
|
+
persistent = [message for message in messages if message.get("turns_left", -1) == -1]
|
|
474
|
+
temporary = [
|
|
475
|
+
message for message in messages
|
|
476
|
+
if isinstance(message.get("turns_left", -1), int) and message.get("turns_left", -1) > 0
|
|
477
|
+
]
|
|
478
|
+
config = modict(self.cache.get("config") or {})
|
|
479
|
+
input_limit = int(status.get("context_limit_tokens") or config.get("input_token_limit", 0) or 0)
|
|
480
|
+
if not current_tokens:
|
|
481
|
+
current_tokens = estimated_base_tokens + active_tokens
|
|
482
|
+
base_tokens = max(0, current_tokens - active_tokens) if current_tokens else estimated_base_tokens
|
|
483
|
+
status.update(
|
|
484
|
+
context_current_tokens=current_tokens,
|
|
485
|
+
context_limit_tokens=input_limit,
|
|
486
|
+
context_percent=(current_tokens / input_limit * 100) if input_limit else 0,
|
|
487
|
+
model=status.get("model") or config.get("model"),
|
|
488
|
+
source="process_fallback",
|
|
489
|
+
total_session_messages=len(messages) or status.get("total_session_messages", 0),
|
|
490
|
+
persistent_session_messages=len(persistent),
|
|
491
|
+
temporary_session_messages=len(temporary),
|
|
492
|
+
active_wrapper_messages=len(active),
|
|
493
|
+
vanished_wrapper_messages=len(vanished),
|
|
494
|
+
total_wrapper_messages=len(wrappers),
|
|
495
|
+
active_wrapper_tokens=active_tokens,
|
|
496
|
+
vanished_wrapper_tokens=vanished_tokens,
|
|
497
|
+
base_context_tokens=base_tokens,
|
|
498
|
+
)
|
|
499
|
+
self.cache.status = status
|
|
500
|
+
return status
|
|
501
|
+
|
|
502
|
+
def has_worker_status_cache(self):
|
|
503
|
+
status = modict(self.cache.get("status") or {})
|
|
504
|
+
source = status.get("source")
|
|
505
|
+
return bool(source) and source != "process_fallback"
|
|
506
|
+
|
|
507
|
+
def append_cached_message(self, message):
|
|
508
|
+
message = self.cached_message_from_event(message)
|
|
509
|
+
if message is None:
|
|
510
|
+
return None
|
|
511
|
+
self.cache.messages.append(message)
|
|
512
|
+
if not self.has_worker_status_cache():
|
|
513
|
+
return self.refresh_cached_wrapper_status()
|
|
514
|
+
return self.cache.status
|
|
515
|
+
|
|
516
|
+
def age_cached_context_messages(self):
|
|
517
|
+
changed = False
|
|
518
|
+
for message in self.cache.get("messages") or []:
|
|
519
|
+
turns_left = message.get("turns_left", -1)
|
|
520
|
+
if isinstance(turns_left, int) and turns_left > 0:
|
|
521
|
+
message.turns_left = turns_left - 1
|
|
522
|
+
changed = True
|
|
523
|
+
if changed and not self.has_worker_status_cache():
|
|
524
|
+
self.refresh_cached_wrapper_status()
|
|
525
|
+
|
|
394
526
|
def mark_turn_started(self, turn_id=None):
|
|
395
527
|
self.cache.busy = True
|
|
396
528
|
if turn_id is not None:
|
|
@@ -404,12 +536,22 @@ class ProcessAgentRuntime(AgentRuntime):
|
|
|
404
536
|
event_type = event.get("type")
|
|
405
537
|
if event_type == "assistant_turn_start_event":
|
|
406
538
|
self.mark_turn_started(event.get("turn_id"))
|
|
539
|
+
self.age_cached_context_messages()
|
|
407
540
|
elif event_type in ("assistant_turn_end_event", "assistant_turn_error_event"):
|
|
408
541
|
self.mark_turn_finished()
|
|
542
|
+
elif event_type == "message_added_event":
|
|
543
|
+
message = self.cached_message_from_event(event.get("message"))
|
|
544
|
+
if message is not None:
|
|
545
|
+
self.cache.messages.append(message)
|
|
546
|
+
if not self.has_worker_status_cache():
|
|
547
|
+
self.refresh_cached_wrapper_status()
|
|
409
548
|
elif event_type == "session_loaded_event":
|
|
410
549
|
self.cache.current_session_id = event.get("session_id")
|
|
411
550
|
self.cache.session = event.get("session") or self.cache.session
|
|
412
|
-
|
|
551
|
+
self.cache.messages = list((self.cache.session or {}).get("messages") or [])
|
|
552
|
+
if not self.has_worker_status_cache():
|
|
553
|
+
self.refresh_cached_wrapper_status()
|
|
554
|
+
elif event_type in ("status_updated_event", "compaction_completed_event") and event.get("status"):
|
|
413
555
|
self.update_status_cache(event.status)
|
|
414
556
|
for handler in list(self.event_handlers):
|
|
415
557
|
try:
|
|
@@ -434,8 +576,13 @@ class ProcessAgentRuntime(AgentRuntime):
|
|
|
434
576
|
return self.update_status_cache(self.call("get_status", timeout=self.status_timeout))
|
|
435
577
|
except Exception:
|
|
436
578
|
status = modict(self.cache.status or {})
|
|
437
|
-
|
|
438
|
-
|
|
579
|
+
if not self.has_worker_status_cache():
|
|
580
|
+
status = self.refresh_cached_wrapper_status()
|
|
581
|
+
status.update(
|
|
582
|
+
busy=self.cache.busy,
|
|
583
|
+
current_turn_id=self.cache.current_turn_id,
|
|
584
|
+
session_id=self.cache.current_session_id,
|
|
585
|
+
)
|
|
439
586
|
return status
|
|
440
587
|
|
|
441
588
|
def config(self):
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
from modict import modict
|
|
2
2
|
|
|
3
3
|
from .image import ImageMessage
|
|
4
|
-
from .message import Message, ProviderMessage, SystemMessage
|
|
4
|
+
from .message import Message, ProviderMessage, SystemMessage, ToolResultWrapper
|
|
5
5
|
from .tool import reset_current_agent, set_current_agent
|
|
6
6
|
from .utils import sort, token_count_payload, truncate
|
|
7
7
|
|
|
@@ -100,14 +100,45 @@ class ContextManager:
|
|
|
100
100
|
formatted_context = list(msg.format(context=self.agent.context_namespace) for msg in context)
|
|
101
101
|
total_session_messages = len(self.agent.session.messages) if self.agent.session else 0
|
|
102
102
|
sent_session_messages = len(history) + len(images)
|
|
103
|
+
all_session_messages = list(self.agent.session.messages) if self.agent.session else []
|
|
103
104
|
persistent_session_messages = sum(
|
|
104
|
-
1 for msg in
|
|
105
|
+
1 for msg in all_session_messages
|
|
105
106
|
if msg.get("turns_left", -1) == -1
|
|
106
107
|
)
|
|
107
108
|
temporary_session_messages = sum(
|
|
108
|
-
1 for msg in
|
|
109
|
+
1 for msg in all_session_messages
|
|
109
110
|
if isinstance(msg.get("turns_left", -1), int) and msg.get("turns_left", -1) > 0
|
|
110
111
|
)
|
|
112
|
+
wrappers = [msg for msg in all_session_messages if isinstance(msg, ToolResultWrapper)]
|
|
113
|
+
active_wrappers = [
|
|
114
|
+
msg for msg in wrappers
|
|
115
|
+
if isinstance(msg.get("turns_left", -1), int) and msg.get("turns_left", -1) > 0
|
|
116
|
+
]
|
|
117
|
+
vanished_wrappers = [msg for msg in wrappers if msg.get("turns_left", -1) == 0]
|
|
118
|
+
|
|
119
|
+
def wrapper_token_count(messages):
|
|
120
|
+
total = 0
|
|
121
|
+
for msg in messages:
|
|
122
|
+
total += self.response_input_token_count([msg], response_tools=[])
|
|
123
|
+
return total
|
|
124
|
+
|
|
125
|
+
active_context_wrappers = [msg for msg in history if isinstance(msg, ToolResultWrapper)]
|
|
126
|
+
active_wrapper_tokens = wrapper_token_count(active_context_wrappers)
|
|
127
|
+
vanished_wrapper_tokens = wrapper_token_count(vanished_wrappers)
|
|
128
|
+
base_context_tokens = max(0, current_count - active_wrapper_tokens)
|
|
129
|
+
if self.agent.session:
|
|
130
|
+
compaction_plan = self.agent.session.compaction_plan(
|
|
131
|
+
max_input_tokens=max_input_tokens,
|
|
132
|
+
model=self.agent.config.model,
|
|
133
|
+
max_compacted_turns=None,
|
|
134
|
+
)
|
|
135
|
+
compactable_turns = compaction_plan.completed_turn_count
|
|
136
|
+
compactable_messages = compaction_plan.compacted_message_count
|
|
137
|
+
compactable_tokens = compaction_plan.compacted_token_count
|
|
138
|
+
else:
|
|
139
|
+
compactable_turns = 0
|
|
140
|
+
compactable_messages = 0
|
|
141
|
+
compactable_tokens = 0
|
|
111
142
|
token_count_source = "local_estimate"
|
|
112
143
|
try:
|
|
113
144
|
current_count = self.agent.ai.count_input_tokens(
|
|
@@ -125,6 +156,15 @@ class ContextManager:
|
|
|
125
156
|
sent_session_message_percent=(sent_session_messages / total_session_messages * 100) if total_session_messages else 0,
|
|
126
157
|
persistent_session_messages=persistent_session_messages,
|
|
127
158
|
temporary_session_messages=temporary_session_messages,
|
|
159
|
+
active_wrapper_messages=len(active_wrappers),
|
|
160
|
+
vanished_wrapper_messages=len(vanished_wrappers),
|
|
161
|
+
total_wrapper_messages=len(wrappers),
|
|
162
|
+
base_context_tokens=base_context_tokens,
|
|
163
|
+
active_wrapper_tokens=active_wrapper_tokens,
|
|
164
|
+
vanished_wrapper_tokens=vanished_wrapper_tokens,
|
|
165
|
+
compactable_turns=compactable_turns,
|
|
166
|
+
compactable_messages=compactable_messages,
|
|
167
|
+
compactable_tokens=compactable_tokens,
|
|
128
168
|
token_count_source=token_count_source,
|
|
129
169
|
))
|
|
130
170
|
hook = self.agent.run_hook("context.build.after", messages=formatted_context)
|
|
@@ -396,9 +396,13 @@ class AgentServer:
|
|
|
396
396
|
|
|
397
397
|
def event_stream_status(self, stale_timeout=120):
|
|
398
398
|
with self.event_lock:
|
|
399
|
-
last_event = self.events[-1] if self.events else None
|
|
400
399
|
active = bool(self.turn_replay_active)
|
|
401
400
|
current_turn_events = list(self.current_turn_events)
|
|
401
|
+
source_events = current_turn_events if current_turn_events else list(self.events)
|
|
402
|
+
last_event = next(
|
|
403
|
+
(event for event in reversed(source_events) if event.get("type") != "status_updated_event"),
|
|
404
|
+
None,
|
|
405
|
+
)
|
|
402
406
|
last_timestamp = self.event_timestamp(last_event)
|
|
403
407
|
now = time.time()
|
|
404
408
|
return modict(
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
from modict import modict
|
|
2
2
|
|
|
3
|
+
from .event import StatusUpdatedEvent
|
|
3
4
|
from .image import ImageMessage
|
|
4
5
|
from .message import ToolResultWrapper
|
|
5
6
|
from .utils import sort, truncate
|
|
@@ -12,11 +13,48 @@ class StatusManager:
|
|
|
12
13
|
self.context_status_cache = None
|
|
13
14
|
self.wham_status_loaded = False
|
|
14
15
|
|
|
16
|
+
def publish_status_cache(self):
|
|
17
|
+
if self.status_cache is not None:
|
|
18
|
+
self.agent.emit(StatusUpdatedEvent, status=self.status_cache)
|
|
19
|
+
return self.status_cache
|
|
20
|
+
|
|
21
|
+
def status_from_context_status(self, context_status):
|
|
22
|
+
previous = modict(self.status_cache or {})
|
|
23
|
+
return modict(
|
|
24
|
+
context_current_tokens=context_status.used_tokens,
|
|
25
|
+
context_limit_tokens=context_status.input_token_limit,
|
|
26
|
+
context_percent=context_status.percent,
|
|
27
|
+
sent_session_messages=context_status.sent_session_messages,
|
|
28
|
+
total_session_messages=context_status.total_session_messages,
|
|
29
|
+
sent_session_message_percent=context_status.sent_session_message_percent,
|
|
30
|
+
persistent_session_messages=context_status.get("persistent_session_messages", 0),
|
|
31
|
+
temporary_session_messages=context_status.get("temporary_session_messages", 0),
|
|
32
|
+
active_wrapper_messages=context_status.get("active_wrapper_messages", 0),
|
|
33
|
+
vanished_wrapper_messages=context_status.get("vanished_wrapper_messages", 0),
|
|
34
|
+
total_wrapper_messages=context_status.get("total_wrapper_messages", 0),
|
|
35
|
+
base_context_tokens=context_status.get("base_context_tokens", context_status.used_tokens),
|
|
36
|
+
active_wrapper_tokens=context_status.get("active_wrapper_tokens", 0),
|
|
37
|
+
vanished_wrapper_tokens=context_status.get("vanished_wrapper_tokens", 0),
|
|
38
|
+
compactable_turns=context_status.get("compactable_turns", 0),
|
|
39
|
+
compactable_messages=context_status.get("compactable_messages", 0),
|
|
40
|
+
compactable_tokens=context_status.get("compactable_tokens", 0),
|
|
41
|
+
quota_5h_percent=previous.get("quota_5h_percent"),
|
|
42
|
+
quota_7d_percent=previous.get("quota_7d_percent"),
|
|
43
|
+
model=previous.get("model") or self.agent.config.get("model") or "gpt-5.4",
|
|
44
|
+
busy=self.agent.mainloop_manager.busy,
|
|
45
|
+
current_turn_id=self.agent.mainloop_manager.current_turn_id,
|
|
46
|
+
session_id=self.agent.current_session_id,
|
|
47
|
+
source=context_status.get("token_count_source", "estimated"),
|
|
48
|
+
activity=self.agent.mainloop_manager.activity_status(),
|
|
49
|
+
)
|
|
50
|
+
|
|
15
51
|
def invalidate_context_cache(self):
|
|
16
52
|
self.context_status_cache = None
|
|
17
53
|
|
|
18
54
|
def cache_context_status(self, status):
|
|
19
55
|
self.context_status_cache = status
|
|
56
|
+
self.status_cache = self.status_from_context_status(status)
|
|
57
|
+
self.publish_status_cache()
|
|
20
58
|
return status
|
|
21
59
|
|
|
22
60
|
def estimated_context_status(self):
|
|
@@ -66,11 +104,11 @@ class StatusManager:
|
|
|
66
104
|
sent_session_messages = visible_history_count + len(images)
|
|
67
105
|
total_session_messages = len(self.agent.session.messages) if self.agent.session else 0
|
|
68
106
|
persistent_session_messages = sum(
|
|
69
|
-
1 for msg in
|
|
107
|
+
1 for msg in all_session_messages
|
|
70
108
|
if msg.get("turns_left", -1) == -1
|
|
71
109
|
)
|
|
72
110
|
temporary_session_messages = sum(
|
|
73
|
-
1 for msg in
|
|
111
|
+
1 for msg in all_session_messages
|
|
74
112
|
if isinstance(msg.get("turns_left", -1), int) and msg.get("turns_left", -1) > 0
|
|
75
113
|
)
|
|
76
114
|
wrappers = [msg for msg in all_session_messages if isinstance(msg, ToolResultWrapper)]
|
|
@@ -142,44 +180,16 @@ class StatusManager:
|
|
|
142
180
|
def get_status(self):
|
|
143
181
|
if self.status_cache is None and not self.wham_status_loaded:
|
|
144
182
|
self.refresh_wham_status()
|
|
145
|
-
if self.status_cache is
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
current_turn_id=self.agent.mainloop_manager.current_turn_id,
|
|
150
|
-
session_id=self.agent.current_session_id,
|
|
151
|
-
activity=self.agent.mainloop_manager.activity_status(),
|
|
152
|
-
)
|
|
153
|
-
return status
|
|
154
|
-
|
|
155
|
-
estimated = self.estimated_context_status()
|
|
156
|
-
return modict(
|
|
157
|
-
context_current_tokens=estimated.used_tokens,
|
|
158
|
-
context_limit_tokens=estimated.input_token_limit,
|
|
159
|
-
context_percent=estimated.percent,
|
|
160
|
-
sent_session_messages=estimated.sent_session_messages,
|
|
161
|
-
total_session_messages=estimated.total_session_messages,
|
|
162
|
-
sent_session_message_percent=estimated.sent_session_message_percent,
|
|
163
|
-
persistent_session_messages=estimated.get("persistent_session_messages", 0),
|
|
164
|
-
temporary_session_messages=estimated.get("temporary_session_messages", 0),
|
|
165
|
-
active_wrapper_messages=estimated.get("active_wrapper_messages", 0),
|
|
166
|
-
vanished_wrapper_messages=estimated.get("vanished_wrapper_messages", 0),
|
|
167
|
-
total_wrapper_messages=estimated.get("total_wrapper_messages", 0),
|
|
168
|
-
base_context_tokens=estimated.get("base_context_tokens", estimated.used_tokens),
|
|
169
|
-
active_wrapper_tokens=estimated.get("active_wrapper_tokens", 0),
|
|
170
|
-
vanished_wrapper_tokens=estimated.get("vanished_wrapper_tokens", 0),
|
|
171
|
-
compactable_turns=estimated.get("compactable_turns", 0),
|
|
172
|
-
compactable_messages=estimated.get("compactable_messages", 0),
|
|
173
|
-
compactable_tokens=estimated.get("compactable_tokens", 0),
|
|
174
|
-
quota_5h_percent=None,
|
|
175
|
-
quota_7d_percent=None,
|
|
176
|
-
model=self.agent.config.get("model") or "gpt-5.4",
|
|
183
|
+
if self.status_cache is None:
|
|
184
|
+
self.context_status()
|
|
185
|
+
status = modict(self.status_cache or {})
|
|
186
|
+
status.update(
|
|
177
187
|
busy=self.agent.mainloop_manager.busy,
|
|
178
188
|
current_turn_id=self.agent.mainloop_manager.current_turn_id,
|
|
179
189
|
session_id=self.agent.current_session_id,
|
|
180
|
-
source=estimated.get("token_count_source", "estimated"),
|
|
181
190
|
activity=self.agent.mainloop_manager.activity_status(),
|
|
182
191
|
)
|
|
192
|
+
return status
|
|
183
193
|
|
|
184
194
|
def refresh_wham_status(self):
|
|
185
195
|
self.wham_status_loaded = True
|
|
@@ -215,10 +225,11 @@ class StatusManager:
|
|
|
215
225
|
activity=self.agent.mainloop_manager.activity_status(),
|
|
216
226
|
)
|
|
217
227
|
self.status_cache.update(wham)
|
|
228
|
+
self.publish_status_cache()
|
|
218
229
|
return wham
|
|
219
230
|
|
|
220
231
|
def refresh_estimated_status_cache(self):
|
|
221
|
-
estimated = self.
|
|
232
|
+
estimated = self.context_status()
|
|
222
233
|
if self.status_cache is None:
|
|
223
234
|
return estimated
|
|
224
235
|
previous = self.status_cache.copy()
|
|
@@ -240,7 +251,7 @@ class StatusManager:
|
|
|
240
251
|
compactable_turns=estimated.get("compactable_turns", 0),
|
|
241
252
|
compactable_messages=estimated.get("compactable_messages", 0),
|
|
242
253
|
compactable_tokens=estimated.get("compactable_tokens", 0),
|
|
243
|
-
model=self.agent.config.get("model") or "gpt-5.4",
|
|
254
|
+
model=previous.get("model") or self.agent.config.get("model") or "gpt-5.4",
|
|
244
255
|
busy=self.agent.mainloop_manager.busy,
|
|
245
256
|
current_turn_id=self.agent.mainloop_manager.current_turn_id,
|
|
246
257
|
session_id=self.agent.current_session_id,
|
|
@@ -250,6 +261,7 @@ class StatusManager:
|
|
|
250
261
|
previous.setdefault("quota_5h_percent", None)
|
|
251
262
|
previous.setdefault("quota_7d_percent", None)
|
|
252
263
|
self.status_cache = previous
|
|
264
|
+
self.publish_status_cache()
|
|
253
265
|
return self.status_cache
|
|
254
266
|
|
|
255
267
|
def cache_api_status(self, usage=None, model=None, quota=None):
|
|
@@ -269,7 +281,7 @@ class StatusManager:
|
|
|
269
281
|
active_wrapper_messages=context_status.get("active_wrapper_messages", 0),
|
|
270
282
|
vanished_wrapper_messages=context_status.get("vanished_wrapper_messages", 0),
|
|
271
283
|
total_wrapper_messages=context_status.get("total_wrapper_messages", 0),
|
|
272
|
-
base_context_tokens=context_status.get("
|
|
284
|
+
base_context_tokens=max(0, input_tokens - context_status.get("active_wrapper_tokens", 0)),
|
|
273
285
|
active_wrapper_tokens=context_status.get("active_wrapper_tokens", 0),
|
|
274
286
|
vanished_wrapper_tokens=context_status.get("vanished_wrapper_tokens", 0),
|
|
275
287
|
compactable_turns=context_status.get("compactable_turns", 0),
|
|
@@ -284,6 +296,7 @@ class StatusManager:
|
|
|
284
296
|
source="last_api_call",
|
|
285
297
|
activity=self.agent.mainloop_manager.activity_status(),
|
|
286
298
|
)
|
|
299
|
+
self.publish_status_cache()
|
|
287
300
|
return self.status_cache
|
|
288
301
|
|
|
289
302
|
def wham_status(self, quota=None):
|