gemcode 0.4.15__tar.gz → 0.4.17__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.
- {gemcode-0.4.15/src/gemcode.egg-info → gemcode-0.4.17}/PKG-INFO +7 -1
- {gemcode-0.4.15 → gemcode-0.4.17}/README.md +6 -0
- {gemcode-0.4.15 → gemcode-0.4.17}/pyproject.toml +1 -1
- {gemcode-0.4.15 → gemcode-0.4.17}/src/gemcode/agent_habits.py +26 -6
- {gemcode-0.4.15 → gemcode-0.4.17}/src/gemcode/agent_mesh.py +106 -1
- {gemcode-0.4.15 → gemcode-0.4.17}/src/gemcode/repl_commands.py +1 -0
- {gemcode-0.4.15 → gemcode-0.4.17}/src/gemcode/repl_slash.py +60 -0
- {gemcode-0.4.15 → gemcode-0.4.17}/src/gemcode/tools/__init__.py +28 -0
- {gemcode-0.4.15 → gemcode-0.4.17/src/gemcode.egg-info}/PKG-INFO +7 -1
- {gemcode-0.4.15 → gemcode-0.4.17}/tests/test_agent_mesh.py +26 -0
- {gemcode-0.4.15 → gemcode-0.4.17}/LICENSE +0 -0
- {gemcode-0.4.15 → gemcode-0.4.17}/MANIFEST.in +0 -0
- {gemcode-0.4.15 → gemcode-0.4.17}/setup.cfg +0 -0
- {gemcode-0.4.15 → gemcode-0.4.17}/src/gemcode/__init__.py +0 -0
- {gemcode-0.4.15 → gemcode-0.4.17}/src/gemcode/__main__.py +0 -0
- {gemcode-0.4.15 → gemcode-0.4.17}/src/gemcode/a2a_bridge.py +0 -0
- {gemcode-0.4.15 → gemcode-0.4.17}/src/gemcode/agent.py +0 -0
- {gemcode-0.4.15 → gemcode-0.4.17}/src/gemcode/agent_intelligence.py +0 -0
- {gemcode-0.4.15 → gemcode-0.4.17}/src/gemcode/agent_triggers.py +0 -0
- {gemcode-0.4.15 → gemcode-0.4.17}/src/gemcode/audit.py +0 -0
- {gemcode-0.4.15 → gemcode-0.4.17}/src/gemcode/autocompact.py +0 -0
- {gemcode-0.4.15 → gemcode-0.4.17}/src/gemcode/automations.py +0 -0
- {gemcode-0.4.15 → gemcode-0.4.17}/src/gemcode/autotune.py +0 -0
- {gemcode-0.4.15 → gemcode-0.4.17}/src/gemcode/callbacks.py +0 -0
- {gemcode-0.4.15 → gemcode-0.4.17}/src/gemcode/capability_routing.py +0 -0
- {gemcode-0.4.15 → gemcode-0.4.17}/src/gemcode/checkpoints.py +0 -0
- {gemcode-0.4.15 → gemcode-0.4.17}/src/gemcode/cli.py +0 -0
- {gemcode-0.4.15 → gemcode-0.4.17}/src/gemcode/codebase_awareness.py +0 -0
- {gemcode-0.4.15 → gemcode-0.4.17}/src/gemcode/compaction.py +0 -0
- {gemcode-0.4.15 → gemcode-0.4.17}/src/gemcode/computer_use/__init__.py +0 -0
- {gemcode-0.4.15 → gemcode-0.4.17}/src/gemcode/computer_use/browser_computer.py +0 -0
- {gemcode-0.4.15 → gemcode-0.4.17}/src/gemcode/config.py +0 -0
- {gemcode-0.4.15 → gemcode-0.4.17}/src/gemcode/context_budget.py +0 -0
- {gemcode-0.4.15 → gemcode-0.4.17}/src/gemcode/context_warning.py +0 -0
- {gemcode-0.4.15 → gemcode-0.4.17}/src/gemcode/credentials.py +0 -0
- {gemcode-0.4.15 → gemcode-0.4.17}/src/gemcode/curated_memory.py +0 -0
- {gemcode-0.4.15 → gemcode-0.4.17}/src/gemcode/delegation_learning.py +0 -0
- {gemcode-0.4.15 → gemcode-0.4.17}/src/gemcode/dynamic_policy.py +0 -0
- {gemcode-0.4.15 → gemcode-0.4.17}/src/gemcode/evals/harness.py +0 -0
- {gemcode-0.4.15 → gemcode-0.4.17}/src/gemcode/event_bus.py +0 -0
- {gemcode-0.4.15 → gemcode-0.4.17}/src/gemcode/fleet_reports.py +0 -0
- {gemcode-0.4.15 → gemcode-0.4.17}/src/gemcode/hitl_session.py +0 -0
- {gemcode-0.4.15 → gemcode-0.4.17}/src/gemcode/hooks.py +0 -0
- {gemcode-0.4.15 → gemcode-0.4.17}/src/gemcode/ide_protocol.py +0 -0
- {gemcode-0.4.15 → gemcode-0.4.17}/src/gemcode/ide_stdio.py +0 -0
- {gemcode-0.4.15 → gemcode-0.4.17}/src/gemcode/intent_classifier.py +0 -0
- {gemcode-0.4.15 → gemcode-0.4.17}/src/gemcode/interactions.py +0 -0
- {gemcode-0.4.15 → gemcode-0.4.17}/src/gemcode/invoke.py +0 -0
- {gemcode-0.4.15 → gemcode-0.4.17}/src/gemcode/kaira_client.py +0 -0
- {gemcode-0.4.15 → gemcode-0.4.17}/src/gemcode/kaira_daemon.py +0 -0
- {gemcode-0.4.15 → gemcode-0.4.17}/src/gemcode/kaira_ipc.py +0 -0
- {gemcode-0.4.15 → gemcode-0.4.17}/src/gemcode/kaira_job_store.py +0 -0
- {gemcode-0.4.15 → gemcode-0.4.17}/src/gemcode/learning.py +0 -0
- {gemcode-0.4.15 → gemcode-0.4.17}/src/gemcode/limits.py +0 -0
- {gemcode-0.4.15 → gemcode-0.4.17}/src/gemcode/live_audio_engine.py +0 -0
- {gemcode-0.4.15 → gemcode-0.4.17}/src/gemcode/logging_config.py +0 -0
- {gemcode-0.4.15 → gemcode-0.4.17}/src/gemcode/mcp_loader.py +0 -0
- {gemcode-0.4.15 → gemcode-0.4.17}/src/gemcode/memory/__init__.py +0 -0
- {gemcode-0.4.15 → gemcode-0.4.17}/src/gemcode/memory/embedding_memory_service.py +0 -0
- {gemcode-0.4.15 → gemcode-0.4.17}/src/gemcode/memory/file_memory_service.py +0 -0
- {gemcode-0.4.15 → gemcode-0.4.17}/src/gemcode/modality_tools.py +0 -0
- {gemcode-0.4.15 → gemcode-0.4.17}/src/gemcode/model_errors.py +0 -0
- {gemcode-0.4.15 → gemcode-0.4.17}/src/gemcode/model_routing.py +0 -0
- {gemcode-0.4.15 → gemcode-0.4.17}/src/gemcode/multimodal_input.py +0 -0
- {gemcode-0.4.15 → gemcode-0.4.17}/src/gemcode/openapi_loader.py +0 -0
- {gemcode-0.4.15 → gemcode-0.4.17}/src/gemcode/org.py +0 -0
- {gemcode-0.4.15 → gemcode-0.4.17}/src/gemcode/output_styles.py +0 -0
- {gemcode-0.4.15 → gemcode-0.4.17}/src/gemcode/paths.py +0 -0
- {gemcode-0.4.15 → gemcode-0.4.17}/src/gemcode/permissions.py +0 -0
- {gemcode-0.4.15 → gemcode-0.4.17}/src/gemcode/plugins/__init__.py +0 -0
- {gemcode-0.4.15 → gemcode-0.4.17}/src/gemcode/plugins/terminal_hooks_plugin.py +0 -0
- {gemcode-0.4.15 → gemcode-0.4.17}/src/gemcode/plugins/tool_recovery_plugin.py +0 -0
- {gemcode-0.4.15 → gemcode-0.4.17}/src/gemcode/policy_profile.py +0 -0
- {gemcode-0.4.15 → gemcode-0.4.17}/src/gemcode/pricing.py +0 -0
- {gemcode-0.4.15 → gemcode-0.4.17}/src/gemcode/prompt_suggestions.py +0 -0
- {gemcode-0.4.15 → gemcode-0.4.17}/src/gemcode/query/__init__.py +0 -0
- {gemcode-0.4.15 → gemcode-0.4.17}/src/gemcode/query/config.py +0 -0
- {gemcode-0.4.15 → gemcode-0.4.17}/src/gemcode/query/deps.py +0 -0
- {gemcode-0.4.15 → gemcode-0.4.17}/src/gemcode/query/engine.py +0 -0
- {gemcode-0.4.15 → gemcode-0.4.17}/src/gemcode/query/stop_hooks.py +0 -0
- {gemcode-0.4.15 → gemcode-0.4.17}/src/gemcode/query/token_budget.py +0 -0
- {gemcode-0.4.15 → gemcode-0.4.17}/src/gemcode/query/transitions.py +0 -0
- {gemcode-0.4.15 → gemcode-0.4.17}/src/gemcode/query_sanitizer.py +0 -0
- {gemcode-0.4.15 → gemcode-0.4.17}/src/gemcode/refine.py +0 -0
- {gemcode-0.4.15 → gemcode-0.4.17}/src/gemcode/review_agent.py +0 -0
- {gemcode-0.4.15 → gemcode-0.4.17}/src/gemcode/rules.py +0 -0
- {gemcode-0.4.15 → gemcode-0.4.17}/src/gemcode/self_healing.py +0 -0
- {gemcode-0.4.15 → gemcode-0.4.17}/src/gemcode/session_runtime.py +0 -0
- {gemcode-0.4.15 → gemcode-0.4.17}/src/gemcode/session_store.py +0 -0
- {gemcode-0.4.15 → gemcode-0.4.17}/src/gemcode/session_summariser.py +0 -0
- {gemcode-0.4.15 → gemcode-0.4.17}/src/gemcode/skills.py +0 -0
- {gemcode-0.4.15 → gemcode-0.4.17}/src/gemcode/slash_commands.py +0 -0
- {gemcode-0.4.15 → gemcode-0.4.17}/src/gemcode/thinking.py +0 -0
- {gemcode-0.4.15 → gemcode-0.4.17}/src/gemcode/tool_prompt_manifest.py +0 -0
- {gemcode-0.4.15 → gemcode-0.4.17}/src/gemcode/tool_registry.py +0 -0
- {gemcode-0.4.15 → gemcode-0.4.17}/src/gemcode/tool_result_store.py +0 -0
- {gemcode-0.4.15 → gemcode-0.4.17}/src/gemcode/tool_synthesis.py +0 -0
- {gemcode-0.4.15 → gemcode-0.4.17}/src/gemcode/tools/automations_tools.py +0 -0
- {gemcode-0.4.15 → gemcode-0.4.17}/src/gemcode/tools/bash.py +0 -0
- {gemcode-0.4.15 → gemcode-0.4.17}/src/gemcode/tools/browser.py +0 -0
- {gemcode-0.4.15 → gemcode-0.4.17}/src/gemcode/tools/compress_memory.py +0 -0
- {gemcode-0.4.15 → gemcode-0.4.17}/src/gemcode/tools/curated_memory.py +0 -0
- {gemcode-0.4.15 → gemcode-0.4.17}/src/gemcode/tools/edit.py +0 -0
- {gemcode-0.4.15 → gemcode-0.4.17}/src/gemcode/tools/filesystem.py +0 -0
- {gemcode-0.4.15 → gemcode-0.4.17}/src/gemcode/tools/notebook.py +0 -0
- {gemcode-0.4.15 → gemcode-0.4.17}/src/gemcode/tools/notes.py +0 -0
- {gemcode-0.4.15 → gemcode-0.4.17}/src/gemcode/tools/org_tools.py +0 -0
- {gemcode-0.4.15 → gemcode-0.4.17}/src/gemcode/tools/repo_map.py +0 -0
- {gemcode-0.4.15 → gemcode-0.4.17}/src/gemcode/tools/search.py +0 -0
- {gemcode-0.4.15 → gemcode-0.4.17}/src/gemcode/tools/shell.py +0 -0
- {gemcode-0.4.15 → gemcode-0.4.17}/src/gemcode/tools/shell_gate.py +0 -0
- {gemcode-0.4.15 → gemcode-0.4.17}/src/gemcode/tools/skills.py +0 -0
- {gemcode-0.4.15 → gemcode-0.4.17}/src/gemcode/tools/subtask.py +0 -0
- {gemcode-0.4.15 → gemcode-0.4.17}/src/gemcode/tools/tasks.py +0 -0
- {gemcode-0.4.15 → gemcode-0.4.17}/src/gemcode/tools/think.py +0 -0
- {gemcode-0.4.15 → gemcode-0.4.17}/src/gemcode/tools/todo.py +0 -0
- {gemcode-0.4.15 → gemcode-0.4.17}/src/gemcode/tools/user_choice.py +0 -0
- {gemcode-0.4.15 → gemcode-0.4.17}/src/gemcode/tools/veomem_tools.py +0 -0
- {gemcode-0.4.15 → gemcode-0.4.17}/src/gemcode/tools/web.py +0 -0
- {gemcode-0.4.15 → gemcode-0.4.17}/src/gemcode/tools/web_search.py +0 -0
- {gemcode-0.4.15 → gemcode-0.4.17}/src/gemcode/tools_inspector.py +0 -0
- {gemcode-0.4.15 → gemcode-0.4.17}/src/gemcode/trust.py +0 -0
- {gemcode-0.4.15 → gemcode-0.4.17}/src/gemcode/tui/input_handler.py +0 -0
- {gemcode-0.4.15 → gemcode-0.4.17}/src/gemcode/tui/scrollback.py +0 -0
- {gemcode-0.4.15 → gemcode-0.4.17}/src/gemcode/tui/spinner.py +0 -0
- {gemcode-0.4.15 → gemcode-0.4.17}/src/gemcode/tui/welcome_banner.py +0 -0
- {gemcode-0.4.15 → gemcode-0.4.17}/src/gemcode/tui/welcome_rich.py +0 -0
- {gemcode-0.4.15 → gemcode-0.4.17}/src/gemcode/veomem_bridge.py +0 -0
- {gemcode-0.4.15 → gemcode-0.4.17}/src/gemcode/version.py +0 -0
- {gemcode-0.4.15 → gemcode-0.4.17}/src/gemcode/vertex.py +0 -0
- {gemcode-0.4.15 → gemcode-0.4.17}/src/gemcode/wal.py +0 -0
- {gemcode-0.4.15 → gemcode-0.4.17}/src/gemcode/web/__init__.py +0 -0
- {gemcode-0.4.15 → gemcode-0.4.17}/src/gemcode/web/sse_adapter.py +0 -0
- {gemcode-0.4.15 → gemcode-0.4.17}/src/gemcode/web/terminal_repl.py +0 -0
- {gemcode-0.4.15 → gemcode-0.4.17}/src/gemcode/web/web_sse_compat.py +0 -0
- {gemcode-0.4.15 → gemcode-0.4.17}/src/gemcode/workspace_hints.py +0 -0
- {gemcode-0.4.15 → gemcode-0.4.17}/src/gemcode.egg-info/SOURCES.txt +0 -0
- {gemcode-0.4.15 → gemcode-0.4.17}/src/gemcode.egg-info/dependency_links.txt +0 -0
- {gemcode-0.4.15 → gemcode-0.4.17}/src/gemcode.egg-info/entry_points.txt +0 -0
- {gemcode-0.4.15 → gemcode-0.4.17}/src/gemcode.egg-info/requires.txt +0 -0
- {gemcode-0.4.15 → gemcode-0.4.17}/src/gemcode.egg-info/top_level.txt +0 -0
- {gemcode-0.4.15 → gemcode-0.4.17}/tests/test_add_dir.py +0 -0
- {gemcode-0.4.15 → gemcode-0.4.17}/tests/test_agent_habits.py +0 -0
- {gemcode-0.4.15 → gemcode-0.4.17}/tests/test_agent_instruction.py +0 -0
- {gemcode-0.4.15 → gemcode-0.4.17}/tests/test_autocompact.py +0 -0
- {gemcode-0.4.15 → gemcode-0.4.17}/tests/test_automations.py +0 -0
- {gemcode-0.4.15 → gemcode-0.4.17}/tests/test_capability_routing.py +0 -0
- {gemcode-0.4.15 → gemcode-0.4.17}/tests/test_checkpoint_diff_command.py +0 -0
- {gemcode-0.4.15 → gemcode-0.4.17}/tests/test_cli_init.py +0 -0
- {gemcode-0.4.15 → gemcode-0.4.17}/tests/test_compress_memory_tool.py +0 -0
- {gemcode-0.4.15 → gemcode-0.4.17}/tests/test_computer_use_permissions.py +0 -0
- {gemcode-0.4.15 → gemcode-0.4.17}/tests/test_context_budget.py +0 -0
- {gemcode-0.4.15 → gemcode-0.4.17}/tests/test_context_warning.py +0 -0
- {gemcode-0.4.15 → gemcode-0.4.17}/tests/test_credentials.py +0 -0
- {gemcode-0.4.15 → gemcode-0.4.17}/tests/test_eval_harness_layout.py +0 -0
- {gemcode-0.4.15 → gemcode-0.4.17}/tests/test_event_bus.py +0 -0
- {gemcode-0.4.15 → gemcode-0.4.17}/tests/test_fleet_reports.py +0 -0
- {gemcode-0.4.15 → gemcode-0.4.17}/tests/test_ide_stdio_attachments.py +0 -0
- {gemcode-0.4.15 → gemcode-0.4.17}/tests/test_interactive_permission_ask.py +0 -0
- {gemcode-0.4.15 → gemcode-0.4.17}/tests/test_kaira_ipc_paths.py +0 -0
- {gemcode-0.4.15 → gemcode-0.4.17}/tests/test_kaira_scheduler.py +0 -0
- {gemcode-0.4.15 → gemcode-0.4.17}/tests/test_modality_tools.py +0 -0
- {gemcode-0.4.15 → gemcode-0.4.17}/tests/test_model_error_retry.py +0 -0
- {gemcode-0.4.15 → gemcode-0.4.17}/tests/test_model_errors.py +0 -0
- {gemcode-0.4.15 → gemcode-0.4.17}/tests/test_model_routing.py +0 -0
- {gemcode-0.4.15 → gemcode-0.4.17}/tests/test_multimodal_input.py +0 -0
- {gemcode-0.4.15 → gemcode-0.4.17}/tests/test_output_styles_and_rules.py +0 -0
- {gemcode-0.4.15 → gemcode-0.4.17}/tests/test_paths.py +0 -0
- {gemcode-0.4.15 → gemcode-0.4.17}/tests/test_permissions.py +0 -0
- {gemcode-0.4.15 → gemcode-0.4.17}/tests/test_prompt_suggestions.py +0 -0
- {gemcode-0.4.15 → gemcode-0.4.17}/tests/test_repl_commands.py +0 -0
- {gemcode-0.4.15 → gemcode-0.4.17}/tests/test_repl_slash.py +0 -0
- {gemcode-0.4.15 → gemcode-0.4.17}/tests/test_session_runtime_cache.py +0 -0
- {gemcode-0.4.15 → gemcode-0.4.17}/tests/test_skills.py +0 -0
- {gemcode-0.4.15 → gemcode-0.4.17}/tests/test_slash_commands.py +0 -0
- {gemcode-0.4.15 → gemcode-0.4.17}/tests/test_slash_completion_registry.py +0 -0
- {gemcode-0.4.15 → gemcode-0.4.17}/tests/test_thinking_config.py +0 -0
- {gemcode-0.4.15 → gemcode-0.4.17}/tests/test_token_budget.py +0 -0
- {gemcode-0.4.15 → gemcode-0.4.17}/tests/test_tool_context_circulation.py +0 -0
- {gemcode-0.4.15 → gemcode-0.4.17}/tests/test_tools.py +0 -0
- {gemcode-0.4.15 → gemcode-0.4.17}/tests/test_tools_inspector.py +0 -0
- {gemcode-0.4.15 → gemcode-0.4.17}/tests/test_web_sse_adapter.py +0 -0
- {gemcode-0.4.15 → gemcode-0.4.17}/tests/test_workspace_hints.py +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: gemcode
|
|
3
|
-
Version: 0.4.
|
|
3
|
+
Version: 0.4.17
|
|
4
4
|
Summary: Local-first coding agent on Google Gemini + ADK
|
|
5
5
|
Author: GemCode Contributors
|
|
6
6
|
License: Apache License
|
|
@@ -292,6 +292,12 @@ Every GemCode run is anchored to a project root. This determines:
|
|
|
292
292
|
- what instruction files are loaded
|
|
293
293
|
- which repo-local assets are active
|
|
294
294
|
|
|
295
|
+
### Multi-agent habits, skills, and mesh runtime
|
|
296
|
+
|
|
297
|
+
- **Habits** for the whole fleet live in **one** file: `.gemcode/habits.json` at the **fleet root**. Each row names which **org member** runs (`agent` field), on what schedule—different members can have different prompts and intervals at once.
|
|
298
|
+
- **Skills** and **per-turn runtime** are **per member**: org `skill_name`, member skill under `.gemcode/skills/`, and optional **agent workspace** `.gemcode/agents/<id>-<slug>/` (own session DB, memory, local skills). Mesh jobs use that context automatically.
|
|
299
|
+
- **Stopping automation:** removing a habit only stops *new* work. To cancel **queued** or **running** mesh jobs, use **`/mesh halt`** or the **`mesh_halt`** tool (see [`orchestration.md`](../docs/orchestration.md#stopping-background-work-habits-removed-but-jobs-still-finishing)).
|
|
300
|
+
|
|
295
301
|
### `.gemcode/`
|
|
296
302
|
GemCode stores project-local state under `.gemcode/`, including:
|
|
297
303
|
- sessions
|
|
@@ -99,6 +99,12 @@ Every GemCode run is anchored to a project root. This determines:
|
|
|
99
99
|
- what instruction files are loaded
|
|
100
100
|
- which repo-local assets are active
|
|
101
101
|
|
|
102
|
+
### Multi-agent habits, skills, and mesh runtime
|
|
103
|
+
|
|
104
|
+
- **Habits** for the whole fleet live in **one** file: `.gemcode/habits.json` at the **fleet root**. Each row names which **org member** runs (`agent` field), on what schedule—different members can have different prompts and intervals at once.
|
|
105
|
+
- **Skills** and **per-turn runtime** are **per member**: org `skill_name`, member skill under `.gemcode/skills/`, and optional **agent workspace** `.gemcode/agents/<id>-<slug>/` (own session DB, memory, local skills). Mesh jobs use that context automatically.
|
|
106
|
+
- **Stopping automation:** removing a habit only stops *new* work. To cancel **queued** or **running** mesh jobs, use **`/mesh halt`** or the **`mesh_halt`** tool (see [`orchestration.md`](../docs/orchestration.md#stopping-background-work-habits-removed-but-jobs-still-finishing)).
|
|
107
|
+
|
|
102
108
|
### `.gemcode/`
|
|
103
109
|
GemCode stores project-local state under `.gemcode/`, including:
|
|
104
110
|
- sessions
|
|
@@ -10,9 +10,18 @@ Examples:
|
|
|
10
10
|
- "Nightly at 2am, run a full security audit"
|
|
11
11
|
- "Every 5 minutes, check for new issues in the tracker"
|
|
12
12
|
|
|
13
|
-
Habits are stored in `.gemcode/habits.json`
|
|
14
|
-
or the REPL.
|
|
15
|
-
|
|
13
|
+
Habits are stored in **one** fleet file, `.gemcode/habits.json` (next to `org.json`),
|
|
14
|
+
and can be managed via tools or the REPL. That file holds **many** rows; each row is
|
|
15
|
+
one schedule and names **which org member** runs it via the ``agent`` field—so
|
|
16
|
+
different agents can have different prompts, intervals, and enablement at the same time.
|
|
17
|
+
|
|
18
|
+
**Not** stored in habits: each member’s **skills** (org `skill_name`, workspace-local
|
|
19
|
+
skills under `.gemcode/agents/<id>-<slug>/.gemcode/skills/`) and **runtime/session**
|
|
20
|
+
(SQLite session, memory, routing). Those come from org membership and the mesh worker
|
|
21
|
+
context when the habit fires.
|
|
22
|
+
|
|
23
|
+
Each habit specifies:
|
|
24
|
+
- Which agent runs it (org member name)
|
|
16
25
|
- What they do (prompt)
|
|
17
26
|
- When they do it (interval, cron, or daily)
|
|
18
27
|
- Whether they're enabled
|
|
@@ -296,7 +305,7 @@ def make_habits_tools(cfg: GemCodeConfig) -> list:
|
|
|
296
305
|
"""Build tools for managing agent habits."""
|
|
297
306
|
|
|
298
307
|
def habits_list() -> dict:
|
|
299
|
-
"""List all
|
|
308
|
+
"""List all fleet habits. Each entry targets one org member (`agent`); members differ by skills/workspace when the job runs."""
|
|
300
309
|
habits = load_habits(cfg.project_root)
|
|
301
310
|
return {
|
|
302
311
|
"ok": True,
|
|
@@ -330,9 +339,14 @@ def make_habits_tools(cfg: GemCodeConfig) -> list:
|
|
|
330
339
|
or send any message; the TUI also prints a throttled hint when mesh jobs finish
|
|
331
340
|
(GEMCODE_FLEET_TUI_NOTIFY).
|
|
332
341
|
|
|
342
|
+
Removing a habit only stops **future** enqueues. Jobs already queued or running (including
|
|
343
|
+
verifier/trigger follow-ups) keep going until they finish unless you call **`mesh_halt`**
|
|
344
|
+
or **`/mesh halt`**.
|
|
345
|
+
|
|
333
346
|
Args:
|
|
334
347
|
name: Unique name for this habit (e.g., "test-watch", "nightly-audit").
|
|
335
|
-
agent: Org member name to run this (e.g., "kaira", "verifier").
|
|
348
|
+
agent: Org **member** name to run this (e.g., "kaira", "verifier", "tcs_analyst").
|
|
349
|
+
That member’s own skills, workspace, and ADK session apply when the habit runs—not the manager’s.
|
|
336
350
|
Use "self" or "main" to run as the main GemCode agent.
|
|
337
351
|
prompt: What the agent should do each time it wakes up.
|
|
338
352
|
every_minutes: Run every N minutes (e.g., 30 = every half hour).
|
|
@@ -391,6 +405,11 @@ def make_habits_tools(cfg: GemCodeConfig) -> list:
|
|
|
391
405
|
save_habits(cfg.project_root, habits)
|
|
392
406
|
return {"ok": True, "removed": before - len(habits)}
|
|
393
407
|
|
|
408
|
+
def habits_clear_all() -> dict:
|
|
409
|
+
"""Remove every habit from `.gemcode/habits.json` (nothing will re-enqueue until you add new habits)."""
|
|
410
|
+
save_habits(cfg.project_root, [])
|
|
411
|
+
return {"ok": True, "cleared": True}
|
|
412
|
+
|
|
394
413
|
def habits_pause(name: str) -> dict:
|
|
395
414
|
"""Pause a habit (stop it from firing until resumed)."""
|
|
396
415
|
habits = load_habits(cfg.project_root)
|
|
@@ -414,7 +433,8 @@ def make_habits_tools(cfg: GemCodeConfig) -> list:
|
|
|
414
433
|
habits_list.__name__ = "habits_list"
|
|
415
434
|
habits_add.__name__ = "habits_add"
|
|
416
435
|
habits_remove.__name__ = "habits_remove"
|
|
436
|
+
habits_clear_all.__name__ = "habits_clear_all"
|
|
417
437
|
habits_pause.__name__ = "habits_pause"
|
|
418
438
|
habits_resume.__name__ = "habits_resume"
|
|
419
439
|
|
|
420
|
-
return [habits_list, habits_add, habits_remove, habits_pause, habits_resume]
|
|
440
|
+
return [habits_list, habits_add, habits_remove, habits_clear_all, habits_pause, habits_resume]
|
|
@@ -9,6 +9,12 @@ that does not require **`gemcode runtime`** for **`org_delegate`**. It manages:
|
|
|
9
9
|
3. Event routing (via the in-memory EventBus)
|
|
10
10
|
4. Automatic result reporting (fleet reports + bus messages)
|
|
11
11
|
|
|
12
|
+
Each queued job is bound to an **org member**. When it runs, that member gets their own
|
|
13
|
+
effective ``project_root`` (agent workspace under ``.gemcode/agents/…`` when configured),
|
|
14
|
+
their **skills** (member skill + workspace-local skills), **memory**, **SQLite session**,
|
|
15
|
+
and capability/model routing—so different agents do not share one generic runtime even
|
|
16
|
+
when habits or triggers are defined in the same fleet ``habits.json``.
|
|
17
|
+
|
|
12
18
|
Optional **`gemcode runtime`** is a separate fleet-manager process (IPC, automations, stdin queue).
|
|
13
19
|
Slash **`/agent assign`** / **`trigger`** publish `org.assign` over IPC when the socket is up; otherwise the REPL falls back to **`org_delegate`** (this mesh). The mesh also subscribes to **`org.assign`** on the in-process bus for the same payload shape.
|
|
14
20
|
"""
|
|
@@ -24,6 +30,7 @@ import time
|
|
|
24
30
|
import uuid
|
|
25
31
|
from dataclasses import dataclass, field
|
|
26
32
|
from pathlib import Path
|
|
33
|
+
from collections.abc import Callable
|
|
27
34
|
from typing import Any
|
|
28
35
|
|
|
29
36
|
from gemcode.config import GemCodeConfig, _truthy_env
|
|
@@ -263,6 +270,67 @@ class AgentMesh:
|
|
|
263
270
|
self._bg_loop.call_soon_threadsafe(_poke)
|
|
264
271
|
fut.result(timeout=timeout)
|
|
265
272
|
|
|
273
|
+
def _call_on_mesh_loop(self, fn: Callable[[], Any], *, timeout: float = 30.0) -> Any:
|
|
274
|
+
"""Run a sync callable on the mesh asyncio loop (safe from any thread)."""
|
|
275
|
+
if self._bg_thread is None or not self._bg_thread.is_alive():
|
|
276
|
+
self.start()
|
|
277
|
+
if not self._wait_bg_loop_ready():
|
|
278
|
+
raise RuntimeError("mesh background loop not available")
|
|
279
|
+
assert self._bg_loop is not None
|
|
280
|
+
fut: concurrent.futures.Future[Any] = concurrent.futures.Future()
|
|
281
|
+
|
|
282
|
+
def _wrap() -> None:
|
|
283
|
+
try:
|
|
284
|
+
fut.set_result(fn())
|
|
285
|
+
except Exception as e:
|
|
286
|
+
fut.set_exception(e)
|
|
287
|
+
|
|
288
|
+
self._bg_loop.call_soon_threadsafe(_wrap)
|
|
289
|
+
return fut.result(timeout=timeout)
|
|
290
|
+
|
|
291
|
+
def clear_pending_jobs(self) -> int:
|
|
292
|
+
"""Remove jobs not yet started from the mesh queue. Returns how many were dropped."""
|
|
293
|
+
|
|
294
|
+
def _drain() -> int:
|
|
295
|
+
n = 0
|
|
296
|
+
while True:
|
|
297
|
+
try:
|
|
298
|
+
self._queue.get_nowait()
|
|
299
|
+
n += 1
|
|
300
|
+
except asyncio.QueueEmpty:
|
|
301
|
+
break
|
|
302
|
+
return n
|
|
303
|
+
|
|
304
|
+
return int(self._call_on_mesh_loop(_drain))
|
|
305
|
+
|
|
306
|
+
def cancel_running_jobs(self) -> int:
|
|
307
|
+
"""Cancel in-flight mesh job tasks (habits, delegates, triggers). Returns cancel count."""
|
|
308
|
+
|
|
309
|
+
def _cancel() -> int:
|
|
310
|
+
n = 0
|
|
311
|
+
for _jid, task in list(self._running.items()):
|
|
312
|
+
if not task.done():
|
|
313
|
+
task.cancel()
|
|
314
|
+
n += 1
|
|
315
|
+
return n
|
|
316
|
+
|
|
317
|
+
return int(self._call_on_mesh_loop(_cancel))
|
|
318
|
+
|
|
319
|
+
def halt_jobs(
|
|
320
|
+
self,
|
|
321
|
+
*,
|
|
322
|
+
clear_queue: bool = True,
|
|
323
|
+
cancel_running: bool = True,
|
|
324
|
+
) -> dict[str, Any]:
|
|
325
|
+
"""Stop queued and/or running mesh work (does not edit ``habits.json``)."""
|
|
326
|
+
cleared = self.clear_pending_jobs() if clear_queue else 0
|
|
327
|
+
cancelled = self.cancel_running_jobs() if cancel_running else 0
|
|
328
|
+
return {
|
|
329
|
+
"ok": True,
|
|
330
|
+
"cleared_queued": cleared,
|
|
331
|
+
"cancelled_running": cancelled,
|
|
332
|
+
}
|
|
333
|
+
|
|
266
334
|
def enqueue(
|
|
267
335
|
self,
|
|
268
336
|
*,
|
|
@@ -395,7 +463,10 @@ class AgentMesh:
|
|
|
395
463
|
|
|
396
464
|
# Nested wait=True from inside a running mesh job would deadlock the scheduler
|
|
397
465
|
# (parent holds a concurrency slot while the child waits for another slot).
|
|
398
|
-
|
|
466
|
+
# Only use the inline path on the mesh thread: the manager runs on a different
|
|
467
|
+
# event loop; _mesh_job_depth can be >0 while a habit runs, but inline
|
|
468
|
+
# _run_job_inner must not run there (asyncio locks / ADK are mesh-loop-local).
|
|
469
|
+
if self._is_on_mesh_thread() and self._mesh_job_depth > 0:
|
|
399
470
|
job_id = f"mesh_{uuid.uuid4().hex[:10]}"
|
|
400
471
|
with self._enqueue_lock:
|
|
401
472
|
self._seq += 1
|
|
@@ -600,6 +671,9 @@ class AgentMesh:
|
|
|
600
671
|
pass
|
|
601
672
|
|
|
602
673
|
finally:
|
|
674
|
+
if job.status == "running":
|
|
675
|
+
job.status = "cancelled"
|
|
676
|
+
job.error = "cancelled"
|
|
603
677
|
cf = job.completion_future
|
|
604
678
|
if cf is not None and not cf.done():
|
|
605
679
|
try:
|
|
@@ -868,12 +942,41 @@ class AgentMesh:
|
|
|
868
942
|
_global_mesh: AgentMesh | None = None
|
|
869
943
|
|
|
870
944
|
|
|
945
|
+
def _sync_mesh_cfg_project_root(mesh: AgentMesh, cfg: GemCodeConfig) -> None:
|
|
946
|
+
"""
|
|
947
|
+
Keep the singleton aligned with the active manager ``cfg.project_root``.
|
|
948
|
+
|
|
949
|
+
If the mesh was created from a different cwd than a later ``ensure_mesh`` /
|
|
950
|
+
``get_mesh`` (e.g. ``-C``), habits/triggers/fleet would otherwise read the
|
|
951
|
+
wrong ``.gemcode/`` tree while the UI used another.
|
|
952
|
+
"""
|
|
953
|
+
try:
|
|
954
|
+
cur = Path(mesh.cfg.project_root).resolve()
|
|
955
|
+
nxt = Path(cfg.project_root).resolve()
|
|
956
|
+
if cur == nxt:
|
|
957
|
+
return
|
|
958
|
+
mesh.cfg.project_root = cfg.project_root
|
|
959
|
+
if mesh._trigger_engine is not None:
|
|
960
|
+
mesh._trigger_engine.cfg = mesh.cfg
|
|
961
|
+
mesh._trigger_engine.reload()
|
|
962
|
+
if mesh._habit_scheduler is not None:
|
|
963
|
+
mesh._habit_scheduler.cfg = mesh.cfg
|
|
964
|
+
if mesh._learner is not None:
|
|
965
|
+
mesh._learner.cfg = mesh.cfg
|
|
966
|
+
if mesh._self_healing is not None:
|
|
967
|
+
mesh._self_healing.cfg = mesh.cfg
|
|
968
|
+
except Exception:
|
|
969
|
+
pass
|
|
970
|
+
|
|
971
|
+
|
|
871
972
|
def get_mesh(cfg: GemCodeConfig | None = None) -> AgentMesh | None:
|
|
872
973
|
"""Get or create the global agent mesh."""
|
|
873
974
|
global _global_mesh
|
|
874
975
|
if _global_mesh is None and cfg is not None:
|
|
875
976
|
concurrency = int(os.environ.get("GEMCODE_MESH_CONCURRENCY", "3"))
|
|
876
977
|
_global_mesh = AgentMesh(cfg, max_concurrency=concurrency)
|
|
978
|
+
elif _global_mesh is not None and cfg is not None:
|
|
979
|
+
_sync_mesh_cfg_project_root(_global_mesh, cfg)
|
|
877
980
|
return _global_mesh
|
|
878
981
|
|
|
879
982
|
|
|
@@ -883,6 +986,8 @@ def ensure_mesh(cfg: GemCodeConfig) -> AgentMesh:
|
|
|
883
986
|
if _global_mesh is None:
|
|
884
987
|
concurrency = int(os.environ.get("GEMCODE_MESH_CONCURRENCY", "3"))
|
|
885
988
|
_global_mesh = AgentMesh(cfg, max_concurrency=concurrency)
|
|
989
|
+
else:
|
|
990
|
+
_sync_mesh_cfg_project_root(_global_mesh, cfg)
|
|
886
991
|
return _global_mesh
|
|
887
992
|
|
|
888
993
|
|
|
@@ -237,6 +237,7 @@ SLASH_COMMANDS: list[tuple[str, str]] = [
|
|
|
237
237
|
("bus", "Runtime bus — send/publish lightweight messages over IPC"),
|
|
238
238
|
("inbox", "Bus inbox filters for this UI (to/topics)"),
|
|
239
239
|
("fleet", "Fleet inbox — /fleet show | digest (habits / mesh reports)"),
|
|
240
|
+
("mesh", "In-process mesh — /mesh halt | status (stop queued/running jobs)"),
|
|
240
241
|
("agent", "Create/manage a child agent workspace (folder + registry)"),
|
|
241
242
|
# NOTE: /org and /delegate are deprecated aliases; keep working but do not list.
|
|
242
243
|
("limits", "Execution limits (calls, context, …)"),
|
|
@@ -146,6 +146,66 @@ async def process_repl_slash(
|
|
|
146
146
|
return ReplSlashResult(skip_model_turn=True)
|
|
147
147
|
return ReplSlashResult(model_prompt=fleet_digest_prompt())
|
|
148
148
|
|
|
149
|
+
# ── /mesh (in-process agent mesh — cancel queued/running work) ─────────────
|
|
150
|
+
if name == "mesh":
|
|
151
|
+
from gemcode.agent_mesh import get_mesh
|
|
152
|
+
|
|
153
|
+
raw_m = (sc.args or "").strip()
|
|
154
|
+
parts_m = raw_m.lower().split()
|
|
155
|
+
first_m = parts_m[0] if parts_m else ""
|
|
156
|
+
|
|
157
|
+
m = get_mesh(cfg)
|
|
158
|
+
if m is None:
|
|
159
|
+
out("[mesh] not initialized.")
|
|
160
|
+
out()
|
|
161
|
+
return ReplSlashResult(skip_model_turn=True)
|
|
162
|
+
|
|
163
|
+
if first_m in ("help", "?"):
|
|
164
|
+
out("In-process agent mesh (habits, org_delegate, triggers):")
|
|
165
|
+
out(" /mesh Show counts + short tip.")
|
|
166
|
+
out(" /mesh status Queued / running job counts.")
|
|
167
|
+
out(" /mesh halt Drop queued jobs and cancel running mesh tasks.")
|
|
168
|
+
out(" /mesh halt --habits Same, and clear `.gemcode/habits.json` entirely.")
|
|
169
|
+
out("Note: `habits_remove` only stops *new* enqueues; queued or running jobs continue")
|
|
170
|
+
out("until they finish unless you `/mesh halt`. Orchestration is in this process")
|
|
171
|
+
out("(no separate daemon required for the mesh).")
|
|
172
|
+
out()
|
|
173
|
+
return ReplSlashResult(skip_model_turn=True)
|
|
174
|
+
|
|
175
|
+
if first_m in ("halt", "stop", "kill"):
|
|
176
|
+
and_habits = "--habits" in parts_m
|
|
177
|
+
try:
|
|
178
|
+
h = m.halt_jobs(clear_queue=True, cancel_running=True)
|
|
179
|
+
except Exception as e:
|
|
180
|
+
out(f"[mesh] halt failed: {type(e).__name__}: {e}")
|
|
181
|
+
out()
|
|
182
|
+
return ReplSlashResult(skip_model_turn=True)
|
|
183
|
+
out(
|
|
184
|
+
f"[mesh] halted: cleared {h.get('cleared_queued', 0)} queued job(s), "
|
|
185
|
+
f"cancelled {h.get('cancelled_running', 0)} running task(s).",
|
|
186
|
+
)
|
|
187
|
+
if and_habits:
|
|
188
|
+
from gemcode.agent_habits import save_habits
|
|
189
|
+
save_habits(cfg.project_root, [])
|
|
190
|
+
out("[mesh] also cleared `.gemcode/habits.json` (all habits removed).")
|
|
191
|
+
out()
|
|
192
|
+
return ReplSlashResult(skip_model_turn=True)
|
|
193
|
+
|
|
194
|
+
if first_m in ("status", "") or not parts_m:
|
|
195
|
+
st = m.status()
|
|
196
|
+
out(
|
|
197
|
+
f"[mesh] queued={st['queued_jobs']} running={st['running_jobs']} "
|
|
198
|
+
f"completed_total={st['completed_jobs']} max_concurrency={st['max_concurrency']}",
|
|
199
|
+
)
|
|
200
|
+
if not parts_m or first_m == "":
|
|
201
|
+
out("Tip: `/mesh halt` stops queued + running work · `/mesh help`")
|
|
202
|
+
out()
|
|
203
|
+
return ReplSlashResult(skip_model_turn=True)
|
|
204
|
+
|
|
205
|
+
out("[mesh] unknown subcommand. Try `/mesh help`.")
|
|
206
|
+
out()
|
|
207
|
+
return ReplSlashResult(skip_model_turn=True)
|
|
208
|
+
|
|
149
209
|
# ── /attach (queue files for the next user message: PDF, images, audio, …) ─
|
|
150
210
|
if name in ("attach", "file", "image", "img"):
|
|
151
211
|
raw_i = (sc.args or "").strip()
|
|
@@ -206,8 +206,36 @@ def build_function_tools(cfg: GemCodeConfig, *, include_subtask: bool = True) ->
|
|
|
206
206
|
return {"ok": False, "error": "mesh not initialized"}
|
|
207
207
|
return {"ok": True, **m.status()}
|
|
208
208
|
|
|
209
|
+
def mesh_halt(
|
|
210
|
+
clear_queued_jobs: bool = True,
|
|
211
|
+
cancel_running_jobs: bool = True,
|
|
212
|
+
remove_all_habits: bool = False,
|
|
213
|
+
) -> dict:
|
|
214
|
+
"""
|
|
215
|
+
Stop background mesh work: drop jobs waiting in the queue, cancel jobs currently running,
|
|
216
|
+
and optionally wipe all habits. Use when habits were removed but work keeps finishing.
|
|
217
|
+
|
|
218
|
+
This is the in-process mesh (same GemCode session), not a separate ``gemcode runtime`` daemon.
|
|
219
|
+
"""
|
|
220
|
+
m = get_mesh(cfg)
|
|
221
|
+
if m is None:
|
|
222
|
+
return {"ok": False, "error": "mesh not initialized"}
|
|
223
|
+
try:
|
|
224
|
+
out = m.halt_jobs(clear_queue=clear_queued_jobs, cancel_running=cancel_running_jobs)
|
|
225
|
+
except Exception as e:
|
|
226
|
+
return {"ok": False, "error": f"{type(e).__name__}: {e}"}
|
|
227
|
+
if remove_all_habits:
|
|
228
|
+
from gemcode.agent_habits import save_habits
|
|
229
|
+
save_habits(cfg.project_root, [])
|
|
230
|
+
out["habits_cleared"] = True
|
|
231
|
+
else:
|
|
232
|
+
out["habits_cleared"] = False
|
|
233
|
+
return out
|
|
234
|
+
|
|
209
235
|
mesh_status.__name__ = "mesh_status"
|
|
236
|
+
mesh_halt.__name__ = "mesh_halt"
|
|
210
237
|
tools.append(mesh_status)
|
|
238
|
+
tools.append(mesh_halt)
|
|
211
239
|
except Exception:
|
|
212
240
|
pass
|
|
213
241
|
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: gemcode
|
|
3
|
-
Version: 0.4.
|
|
3
|
+
Version: 0.4.17
|
|
4
4
|
Summary: Local-first coding agent on Google Gemini + ADK
|
|
5
5
|
Author: GemCode Contributors
|
|
6
6
|
License: Apache License
|
|
@@ -292,6 +292,12 @@ Every GemCode run is anchored to a project root. This determines:
|
|
|
292
292
|
- what instruction files are loaded
|
|
293
293
|
- which repo-local assets are active
|
|
294
294
|
|
|
295
|
+
### Multi-agent habits, skills, and mesh runtime
|
|
296
|
+
|
|
297
|
+
- **Habits** for the whole fleet live in **one** file: `.gemcode/habits.json` at the **fleet root**. Each row names which **org member** runs (`agent` field), on what schedule—different members can have different prompts and intervals at once.
|
|
298
|
+
- **Skills** and **per-turn runtime** are **per member**: org `skill_name`, member skill under `.gemcode/skills/`, and optional **agent workspace** `.gemcode/agents/<id>-<slug>/` (own session DB, memory, local skills). Mesh jobs use that context automatically.
|
|
299
|
+
- **Stopping automation:** removing a habit only stops *new* work. To cancel **queued** or **running** mesh jobs, use **`/mesh halt`** or the **`mesh_halt`** tool (see [`orchestration.md`](../docs/orchestration.md#stopping-background-work-habits-removed-but-jobs-still-finishing)).
|
|
300
|
+
|
|
295
301
|
### `.gemcode/`
|
|
296
302
|
GemCode stores project-local state under `.gemcode/`, including:
|
|
297
303
|
- sessions
|
|
@@ -173,6 +173,32 @@ def test_apply_mesh_worker_unattended_off_inherits_manager(tmp_path: Path) -> No
|
|
|
173
173
|
assert cfg.interactive_permission_ask is True
|
|
174
174
|
|
|
175
175
|
|
|
176
|
+
def test_mesh_halt_clears_pending_queue(tmp_path: Path) -> None:
|
|
177
|
+
old_s = os.environ.get("PYTEST_GEMCODE_MESH_SCHEDULER")
|
|
178
|
+
old_h = os.environ.get("GEMCODE_AGENT_HABITS")
|
|
179
|
+
os.environ["PYTEST_GEMCODE_MESH_SCHEDULER"] = "0"
|
|
180
|
+
os.environ["GEMCODE_AGENT_HABITS"] = "0"
|
|
181
|
+
try:
|
|
182
|
+
cfg = GemCodeConfig(project_root=tmp_path)
|
|
183
|
+
mesh = AgentMesh(cfg, max_concurrency=2)
|
|
184
|
+
mesh.enqueue(prompt="a", priority=1, member_name="x")
|
|
185
|
+
mesh.enqueue(prompt="b", priority=1, member_name="y")
|
|
186
|
+
mesh.wait_for_pending_enqueues()
|
|
187
|
+
assert mesh._queue.qsize() == 2
|
|
188
|
+
h = mesh.halt_jobs(clear_queue=True, cancel_running=False)
|
|
189
|
+
assert h["cleared_queued"] == 2
|
|
190
|
+
assert mesh._queue.qsize() == 0
|
|
191
|
+
finally:
|
|
192
|
+
if old_s is None:
|
|
193
|
+
os.environ.pop("PYTEST_GEMCODE_MESH_SCHEDULER", None)
|
|
194
|
+
else:
|
|
195
|
+
os.environ["PYTEST_GEMCODE_MESH_SCHEDULER"] = old_s
|
|
196
|
+
if old_h is None:
|
|
197
|
+
os.environ.pop("GEMCODE_AGENT_HABITS", None)
|
|
198
|
+
else:
|
|
199
|
+
os.environ["GEMCODE_AGENT_HABITS"] = old_h
|
|
200
|
+
|
|
201
|
+
|
|
176
202
|
def test_mesh_priority_ordering(tmp_path: Path) -> None:
|
|
177
203
|
"""Higher priority jobs should be dequeued first."""
|
|
178
204
|
old_s = os.environ.get("PYTEST_GEMCODE_MESH_SCHEDULER")
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|