gemcode 0.3.26__tar.gz → 0.3.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.
- {gemcode-0.3.26/src/gemcode.egg-info → gemcode-0.3.28}/PKG-INFO +1 -1
- {gemcode-0.3.26 → gemcode-0.3.28}/pyproject.toml +1 -1
- {gemcode-0.3.26 → gemcode-0.3.28}/src/gemcode/agent.py +87 -8
- {gemcode-0.3.26 → gemcode-0.3.28}/src/gemcode/repl_commands.py +1 -0
- {gemcode-0.3.26 → gemcode-0.3.28}/src/gemcode/repl_slash.py +39 -0
- gemcode-0.3.28/src/gemcode/tool_prompt_manifest.py +165 -0
- {gemcode-0.3.26 → gemcode-0.3.28}/src/gemcode/tools/__init__.py +24 -2
- {gemcode-0.3.26 → gemcode-0.3.28}/src/gemcode/tui/input_handler.py +1 -0
- {gemcode-0.3.26 → gemcode-0.3.28}/src/gemcode/tui/scrollback.py +50 -25
- {gemcode-0.3.26 → gemcode-0.3.28/src/gemcode.egg-info}/PKG-INFO +1 -1
- gemcode-0.3.26/src/gemcode/tool_prompt_manifest.py +0 -142
- {gemcode-0.3.26 → gemcode-0.3.28}/LICENSE +0 -0
- {gemcode-0.3.26 → gemcode-0.3.28}/MANIFEST.in +0 -0
- {gemcode-0.3.26 → gemcode-0.3.28}/README.md +0 -0
- {gemcode-0.3.26 → gemcode-0.3.28}/setup.cfg +0 -0
- {gemcode-0.3.26 → gemcode-0.3.28}/src/gemcode/__init__.py +0 -0
- {gemcode-0.3.26 → gemcode-0.3.28}/src/gemcode/__main__.py +0 -0
- {gemcode-0.3.26 → gemcode-0.3.28}/src/gemcode/audit.py +0 -0
- {gemcode-0.3.26 → gemcode-0.3.28}/src/gemcode/autocompact.py +0 -0
- {gemcode-0.3.26 → gemcode-0.3.28}/src/gemcode/callbacks.py +0 -0
- {gemcode-0.3.26 → gemcode-0.3.28}/src/gemcode/capability_routing.py +0 -0
- {gemcode-0.3.26 → gemcode-0.3.28}/src/gemcode/cli.py +0 -0
- {gemcode-0.3.26 → gemcode-0.3.28}/src/gemcode/compaction.py +0 -0
- {gemcode-0.3.26 → gemcode-0.3.28}/src/gemcode/computer_use/__init__.py +0 -0
- {gemcode-0.3.26 → gemcode-0.3.28}/src/gemcode/computer_use/browser_computer.py +0 -0
- {gemcode-0.3.26 → gemcode-0.3.28}/src/gemcode/config.py +0 -0
- {gemcode-0.3.26 → gemcode-0.3.28}/src/gemcode/context_budget.py +0 -0
- {gemcode-0.3.26 → gemcode-0.3.28}/src/gemcode/context_warning.py +0 -0
- {gemcode-0.3.26 → gemcode-0.3.28}/src/gemcode/credentials.py +0 -0
- {gemcode-0.3.26 → gemcode-0.3.28}/src/gemcode/hitl_session.py +0 -0
- {gemcode-0.3.26 → gemcode-0.3.28}/src/gemcode/interactions.py +0 -0
- {gemcode-0.3.26 → gemcode-0.3.28}/src/gemcode/invoke.py +0 -0
- {gemcode-0.3.26 → gemcode-0.3.28}/src/gemcode/kairos_daemon.py +0 -0
- {gemcode-0.3.26 → gemcode-0.3.28}/src/gemcode/limits.py +0 -0
- {gemcode-0.3.26 → gemcode-0.3.28}/src/gemcode/live_audio_engine.py +0 -0
- {gemcode-0.3.26 → gemcode-0.3.28}/src/gemcode/logging_config.py +0 -0
- {gemcode-0.3.26 → gemcode-0.3.28}/src/gemcode/mcp_loader.py +0 -0
- {gemcode-0.3.26 → gemcode-0.3.28}/src/gemcode/memory/__init__.py +0 -0
- {gemcode-0.3.26 → gemcode-0.3.28}/src/gemcode/memory/embedding_memory_service.py +0 -0
- {gemcode-0.3.26 → gemcode-0.3.28}/src/gemcode/memory/file_memory_service.py +0 -0
- {gemcode-0.3.26 → gemcode-0.3.28}/src/gemcode/modality_tools.py +0 -0
- {gemcode-0.3.26 → gemcode-0.3.28}/src/gemcode/model_errors.py +0 -0
- {gemcode-0.3.26 → gemcode-0.3.28}/src/gemcode/model_routing.py +0 -0
- {gemcode-0.3.26 → gemcode-0.3.28}/src/gemcode/paths.py +0 -0
- {gemcode-0.3.26 → gemcode-0.3.28}/src/gemcode/permissions.py +0 -0
- {gemcode-0.3.26 → gemcode-0.3.28}/src/gemcode/plugins/__init__.py +0 -0
- {gemcode-0.3.26 → gemcode-0.3.28}/src/gemcode/plugins/terminal_hooks_plugin.py +0 -0
- {gemcode-0.3.26 → gemcode-0.3.28}/src/gemcode/plugins/tool_recovery_plugin.py +0 -0
- {gemcode-0.3.26 → gemcode-0.3.28}/src/gemcode/prompt_suggestions.py +0 -0
- {gemcode-0.3.26 → gemcode-0.3.28}/src/gemcode/query/__init__.py +0 -0
- {gemcode-0.3.26 → gemcode-0.3.28}/src/gemcode/query/config.py +0 -0
- {gemcode-0.3.26 → gemcode-0.3.28}/src/gemcode/query/deps.py +0 -0
- {gemcode-0.3.26 → gemcode-0.3.28}/src/gemcode/query/engine.py +0 -0
- {gemcode-0.3.26 → gemcode-0.3.28}/src/gemcode/query/stop_hooks.py +0 -0
- {gemcode-0.3.26 → gemcode-0.3.28}/src/gemcode/query/token_budget.py +0 -0
- {gemcode-0.3.26 → gemcode-0.3.28}/src/gemcode/query/transitions.py +0 -0
- {gemcode-0.3.26 → gemcode-0.3.28}/src/gemcode/session_runtime.py +0 -0
- {gemcode-0.3.26 → gemcode-0.3.28}/src/gemcode/slash_commands.py +0 -0
- {gemcode-0.3.26 → gemcode-0.3.28}/src/gemcode/thinking.py +0 -0
- {gemcode-0.3.26 → gemcode-0.3.28}/src/gemcode/tool_registry.py +0 -0
- {gemcode-0.3.26 → gemcode-0.3.28}/src/gemcode/tools/bash.py +0 -0
- {gemcode-0.3.26 → gemcode-0.3.28}/src/gemcode/tools/browser.py +0 -0
- {gemcode-0.3.26 → gemcode-0.3.28}/src/gemcode/tools/edit.py +0 -0
- {gemcode-0.3.26 → gemcode-0.3.28}/src/gemcode/tools/filesystem.py +0 -0
- {gemcode-0.3.26 → gemcode-0.3.28}/src/gemcode/tools/search.py +0 -0
- {gemcode-0.3.26 → gemcode-0.3.28}/src/gemcode/tools/shell.py +0 -0
- {gemcode-0.3.26 → gemcode-0.3.28}/src/gemcode/tools/shell_gate.py +0 -0
- {gemcode-0.3.26 → gemcode-0.3.28}/src/gemcode/tools/subtask.py +0 -0
- {gemcode-0.3.26 → gemcode-0.3.28}/src/gemcode/tools/think.py +0 -0
- {gemcode-0.3.26 → gemcode-0.3.28}/src/gemcode/tools/todo.py +0 -0
- {gemcode-0.3.26 → gemcode-0.3.28}/src/gemcode/tools/web.py +0 -0
- {gemcode-0.3.26 → gemcode-0.3.28}/src/gemcode/tools_inspector.py +0 -0
- {gemcode-0.3.26 → gemcode-0.3.28}/src/gemcode/trust.py +0 -0
- {gemcode-0.3.26 → gemcode-0.3.28}/src/gemcode/tui/spinner.py +0 -0
- {gemcode-0.3.26 → gemcode-0.3.28}/src/gemcode/tui/welcome_banner.py +0 -0
- {gemcode-0.3.26 → gemcode-0.3.28}/src/gemcode/tui/welcome_rich.py +0 -0
- {gemcode-0.3.26 → gemcode-0.3.28}/src/gemcode/version.py +0 -0
- {gemcode-0.3.26 → gemcode-0.3.28}/src/gemcode/vertex.py +0 -0
- {gemcode-0.3.26 → gemcode-0.3.28}/src/gemcode/web/__init__.py +0 -0
- {gemcode-0.3.26 → gemcode-0.3.28}/src/gemcode/web/claude_sse_adapter.py +0 -0
- {gemcode-0.3.26 → gemcode-0.3.28}/src/gemcode/web/terminal_repl.py +0 -0
- {gemcode-0.3.26 → gemcode-0.3.28}/src/gemcode/workspace_hints.py +0 -0
- {gemcode-0.3.26 → gemcode-0.3.28}/src/gemcode.egg-info/SOURCES.txt +0 -0
- {gemcode-0.3.26 → gemcode-0.3.28}/src/gemcode.egg-info/dependency_links.txt +0 -0
- {gemcode-0.3.26 → gemcode-0.3.28}/src/gemcode.egg-info/entry_points.txt +0 -0
- {gemcode-0.3.26 → gemcode-0.3.28}/src/gemcode.egg-info/requires.txt +0 -0
- {gemcode-0.3.26 → gemcode-0.3.28}/src/gemcode.egg-info/top_level.txt +0 -0
- {gemcode-0.3.26 → gemcode-0.3.28}/tests/test_agent_instruction.py +0 -0
- {gemcode-0.3.26 → gemcode-0.3.28}/tests/test_autocompact.py +0 -0
- {gemcode-0.3.26 → gemcode-0.3.28}/tests/test_capability_routing.py +0 -0
- {gemcode-0.3.26 → gemcode-0.3.28}/tests/test_claude_web_adapter_sse.py +0 -0
- {gemcode-0.3.26 → gemcode-0.3.28}/tests/test_cli_init.py +0 -0
- {gemcode-0.3.26 → gemcode-0.3.28}/tests/test_computer_use_permissions.py +0 -0
- {gemcode-0.3.26 → gemcode-0.3.28}/tests/test_context_budget.py +0 -0
- {gemcode-0.3.26 → gemcode-0.3.28}/tests/test_context_warning.py +0 -0
- {gemcode-0.3.26 → gemcode-0.3.28}/tests/test_credentials.py +0 -0
- {gemcode-0.3.26 → gemcode-0.3.28}/tests/test_interactive_permission_ask.py +0 -0
- {gemcode-0.3.26 → gemcode-0.3.28}/tests/test_kairos_scheduler.py +0 -0
- {gemcode-0.3.26 → gemcode-0.3.28}/tests/test_modality_tools.py +0 -0
- {gemcode-0.3.26 → gemcode-0.3.28}/tests/test_model_error_retry.py +0 -0
- {gemcode-0.3.26 → gemcode-0.3.28}/tests/test_model_errors.py +0 -0
- {gemcode-0.3.26 → gemcode-0.3.28}/tests/test_model_routing.py +0 -0
- {gemcode-0.3.26 → gemcode-0.3.28}/tests/test_paths.py +0 -0
- {gemcode-0.3.26 → gemcode-0.3.28}/tests/test_permissions.py +0 -0
- {gemcode-0.3.26 → gemcode-0.3.28}/tests/test_prompt_suggestions.py +0 -0
- {gemcode-0.3.26 → gemcode-0.3.28}/tests/test_repl_commands.py +0 -0
- {gemcode-0.3.26 → gemcode-0.3.28}/tests/test_repl_slash.py +0 -0
- {gemcode-0.3.26 → gemcode-0.3.28}/tests/test_slash_commands.py +0 -0
- {gemcode-0.3.26 → gemcode-0.3.28}/tests/test_thinking_config.py +0 -0
- {gemcode-0.3.26 → gemcode-0.3.28}/tests/test_token_budget.py +0 -0
- {gemcode-0.3.26 → gemcode-0.3.28}/tests/test_tool_context_circulation.py +0 -0
- {gemcode-0.3.26 → gemcode-0.3.28}/tests/test_tools.py +0 -0
- {gemcode-0.3.26 → gemcode-0.3.28}/tests/test_tools_inspector.py +0 -0
- {gemcode-0.3.26 → gemcode-0.3.28}/tests/test_workspace_hints.py +0 -0
|
@@ -58,17 +58,93 @@ def _load_gemini_md(project_root: Path) -> str:
|
|
|
58
58
|
|
|
59
59
|
def _build_runtime_facts(cfg: GemCodeConfig) -> str:
|
|
60
60
|
"""
|
|
61
|
-
Injected every session so the model
|
|
62
|
-
|
|
61
|
+
Injected every session so the model is fully self-aware of its own capabilities,
|
|
62
|
+
limits, and the environment — not just generic defaults.
|
|
63
63
|
"""
|
|
64
64
|
root = cfg.project_root.resolve()
|
|
65
65
|
model = (getattr(cfg, "model", None) or "").strip() or "(default)"
|
|
66
|
+
|
|
67
|
+
# ── Active capabilities ──────────────────────────────────────────────────
|
|
68
|
+
caps: list[str] = []
|
|
69
|
+
if getattr(cfg, "enable_deep_research", False):
|
|
70
|
+
dr_extras = " + google_maps_grounding" if getattr(cfg, "enable_maps_grounding", False) else ""
|
|
71
|
+
caps.append(f"deep_research ON (tools: google_search, url_context{dr_extras})")
|
|
72
|
+
if getattr(cfg, "enable_embeddings", False):
|
|
73
|
+
caps.append(f"embeddings ON (tool: semantic_search_files, model: {getattr(cfg, 'embeddings_model', 'default')})")
|
|
74
|
+
if getattr(cfg, "enable_memory", False):
|
|
75
|
+
mem_path = root / ".gemcode" / "memories.jsonl"
|
|
76
|
+
mem_kind = "embedding-backed" if getattr(cfg, "enable_embeddings", False) else "keyword-backed"
|
|
77
|
+
caps.append(f"memory ON ({mem_kind}, stored at {mem_path}; ADK preload_memory injects relevant memories before each turn)")
|
|
78
|
+
if getattr(cfg, "enable_computer_use", False):
|
|
79
|
+
caps.append("computer_use ON (tools: navigate, click_at, type_text_at, browser_screenshot, browser_find_element, etc.)")
|
|
80
|
+
if not caps:
|
|
81
|
+
caps.append("none enabled (use /research on, /embeddings on, /memory on, /computer on to enable)")
|
|
82
|
+
caps_text = "\n".join(f" - {c}" for c in caps)
|
|
83
|
+
|
|
84
|
+
# ── Limits ───────────────────────────────────────────────────────────────
|
|
85
|
+
max_calls = getattr(cfg, "max_llm_calls", 256) or 256
|
|
86
|
+
token_budget = getattr(cfg, "token_budget", None)
|
|
87
|
+
max_session_tokens = getattr(cfg, "max_session_tokens", None)
|
|
88
|
+
budget_line = f"{max_calls} model↔tool iterations per user message"
|
|
89
|
+
if token_budget:
|
|
90
|
+
budget_line += f" · token_budget={token_budget:,} per turn"
|
|
91
|
+
if max_session_tokens:
|
|
92
|
+
budget_line += f" · max_session_tokens={max_session_tokens:,}"
|
|
93
|
+
|
|
94
|
+
# ── Kairos ────────────────────────────────────────────────────────────────
|
|
95
|
+
# The user can run `gemcode kairos -C <project>` in a separate terminal to
|
|
96
|
+
# launch a long-lived scheduler. Jobs submitted to it run concurrently with
|
|
97
|
+
# the current session. This is useful for background / parallel heavy work.
|
|
98
|
+
kairos_section = (
|
|
99
|
+
"- **Kairos background scheduler** — `gemcode kairos -C <project>` launches a "
|
|
100
|
+
"long-lived daemon that reads prompts from stdin and runs each as an isolated job "
|
|
101
|
+
"(up to N concurrently). Each job gets `kairos_sleep_ms(ms)` and "
|
|
102
|
+
"`kairos_enqueue_prompt(prompt, priority, session_id)` tools so the model can "
|
|
103
|
+
"schedule follow-up work itself. Useful for: bulk file processing, repeated "
|
|
104
|
+
"polling loops, parallelising large independent tasks. "
|
|
105
|
+
"Tell the user to open a second terminal and run `gemcode kairos` if a task "
|
|
106
|
+
"would benefit from background parallelism."
|
|
107
|
+
)
|
|
108
|
+
|
|
66
109
|
return f"""## Runtime facts (authoritative for this session)
|
|
67
110
|
- **Project root** — every filesystem tool path is relative to: `{root}`
|
|
68
|
-
- **Model id in use:** `{model}`.
|
|
69
|
-
- **
|
|
70
|
-
- **
|
|
71
|
-
|
|
111
|
+
- **Model id in use:** `{model}`. Override mid-session with `/model use <id>` or `/mode fast|balanced|quality|auto`.
|
|
112
|
+
- **Execution budget:** {budget_line}.
|
|
113
|
+
- **Active capabilities:**
|
|
114
|
+
{caps_text}
|
|
115
|
+
- **Capability routing** (`capability_mode={getattr(cfg, 'capability_mode', 'auto')}`): in `auto` mode, GemCode automatically enables deep_research when it detects research-intent keywords in your prompt each turn. You can also type `/research on`, `/embeddings on`, `/memory on`, `/computer on` at the prompt.
|
|
116
|
+
- **Your tool palette can grow mid-session:** if the user enables a capability via a slash command, the runner rebuilds and you get new tools on the next turn.
|
|
117
|
+
- **Memory system:** when `memory ON`, ADK automatically searches `.gemcode/memories.jsonl` and injects relevant past context before each turn. Facts the user tells you in one session can appear in future sessions. You do not need to manage memory explicitly — it is loaded automatically.
|
|
118
|
+
{kairos_section}
|
|
119
|
+
- **UI banner** phrases like "GemCode Pro" are terminal marketing, not a separate API tier.
|
|
120
|
+
- **Env toggles** (`GEMCODE_ENABLE_COMPUTER_USE`, `GEMCODE_MODEL`, etc.) affect only the OS process that launched gemcode. Pasting `VAR=1` in chat does NOT reconfigure a running session—tell the user to export in their shell, use project `.env`, or restart the CLI.
|
|
121
|
+
- **Working in subfolders** — call `list_directory("Desktop")`, `glob_files("**/query.ts")`, `read_file("testing/ai-edtech-app/src/app/page.tsx")` directly. Never claim access is blocked unless a tool returned an explicit error."""
|
|
122
|
+
|
|
123
|
+
|
|
124
|
+
def _build_memory_section(cfg: GemCodeConfig) -> str:
|
|
125
|
+
"""Injected when enable_memory=True so the agent understands and uses memory."""
|
|
126
|
+
mem_path = cfg.project_root / ".gemcode" / "memories.jsonl"
|
|
127
|
+
kind = "embedding-based (semantic cosine similarity)" if getattr(cfg, "enable_embeddings", False) else "keyword-based"
|
|
128
|
+
return f"""
|
|
129
|
+
## Persistent Memory System
|
|
130
|
+
Memory is **ON** ({kind}). Stored at: `{mem_path}`
|
|
131
|
+
|
|
132
|
+
### How it works
|
|
133
|
+
- Before each turn, ADK automatically searches the memory store for relevant past facts and injects them as context — you do not need to call a tool to load them.
|
|
134
|
+
- After each turn, the session is automatically added to memory by the post-turn plugin.
|
|
135
|
+
- The memory file persists across sessions (JSONL, one entry per session).
|
|
136
|
+
|
|
137
|
+
### What to do with memory
|
|
138
|
+
- **Reference it naturally** — if the injected context mentions past facts ("last time the user's API key was X", "the user prefers TypeScript"), treat that as trusted context.
|
|
139
|
+
- **Update it proactively** — if the user tells you important facts about their project, preferences, or recurring patterns, note them in your response: "I'll remember that for future sessions."
|
|
140
|
+
- **Don't re-explain already-known context** — if the memory already contains the project structure or preferences, skip the discovery step and act on what's known.
|
|
141
|
+
- **Memory is scoped to this project root** — `{cfg.project_root}`. Different project roots have separate memories.
|
|
142
|
+
|
|
143
|
+
### When memory helps most
|
|
144
|
+
- Long-running projects (user preferences, patterns, recurring tasks)
|
|
145
|
+
- Multi-session workflows (continuing work from a previous day)
|
|
146
|
+
- Team conventions stored once and reused automatically
|
|
147
|
+
"""
|
|
72
148
|
|
|
73
149
|
|
|
74
150
|
def _build_computer_use_section(cfg: GemCodeConfig) -> str:
|
|
@@ -378,10 +454,13 @@ For tasks where quality matters:
|
|
|
378
454
|
## Workspace scope
|
|
379
455
|
All file tools use paths **relative to the project root** (where GemCode was started). The root may be the home folder — subfolders like `Desktop`, `Desktop/code`, `Documents` are inside the sandbox. Call `list_directory("Desktop")` or `glob_files("**/*name*.ts")` instead of assuming access is blocked. Only treat access as denied when a tool returns an explicit `error`."""
|
|
380
456
|
|
|
381
|
-
# Inject
|
|
457
|
+
# Inject capability-specific strategy sections only when those caps are on.
|
|
382
458
|
if getattr(cfg, "enable_computer_use", False):
|
|
383
459
|
base = f"{base}\n\n{_build_computer_use_section(cfg)}"
|
|
384
460
|
|
|
461
|
+
if getattr(cfg, "enable_memory", False):
|
|
462
|
+
base = f"{base}\n\n{_build_memory_section(cfg)}"
|
|
463
|
+
|
|
385
464
|
tool_manifest = build_tool_manifest(cfg)
|
|
386
465
|
if tool_manifest:
|
|
387
466
|
base = f"{base}\n\n{tool_manifest}"
|
|
@@ -409,7 +488,7 @@ def build_root_agent(
|
|
|
409
488
|
if _tools is not None:
|
|
410
489
|
tools = list(_tools)
|
|
411
490
|
else:
|
|
412
|
-
|
|
491
|
+
tools = build_function_tools(cfg)
|
|
413
492
|
if getattr(cfg, "enable_memory", False):
|
|
414
493
|
# ADK preload_memory injects retrieved memories into the next llm_request.
|
|
415
494
|
from google.adk.tools import preload_memory
|
|
@@ -318,6 +318,45 @@ async def process_repl_slash(
|
|
|
318
318
|
if name in ("exit", "quit"):
|
|
319
319
|
return ReplSlashResult(exit_repl=True)
|
|
320
320
|
|
|
321
|
+
# ── /kairos ───────────────────────────────────────────────────────────────
|
|
322
|
+
if name == "kairos":
|
|
323
|
+
args_s = (sc.args or "").strip()
|
|
324
|
+
out("Kairos — background parallel job scheduler")
|
|
325
|
+
out()
|
|
326
|
+
out("What it does:")
|
|
327
|
+
out(" Runs a long-lived daemon that accepts prompts on stdin and executes")
|
|
328
|
+
out(" each as an isolated GemCode agent job. Jobs run concurrently (up to")
|
|
329
|
+
out(" --concurrency N, default 2) in a priority queue.")
|
|
330
|
+
out()
|
|
331
|
+
out("How to launch (in a separate terminal):")
|
|
332
|
+
out(f" gemcode kairos -C {cfg.project_root}")
|
|
333
|
+
out(" gemcode kairos -C <project> --concurrency 4 --yes")
|
|
334
|
+
out(" gemcode kairos -C <project> --model gemini-2.5-pro --deep-research")
|
|
335
|
+
out()
|
|
336
|
+
out("Options:")
|
|
337
|
+
out(" --concurrency N Max parallel jobs (default: 2)")
|
|
338
|
+
out(" --default-priority N Priority for stdin jobs (higher = runs first, default: 0)")
|
|
339
|
+
out(" --yes Auto-approve mutations (like gemcode --yes)")
|
|
340
|
+
out(" --model <id> Override model for all jobs")
|
|
341
|
+
out(" --model-mode <mode> fast|balanced|quality|auto")
|
|
342
|
+
out(" --deep-research Enable Google Search + URL Context for all jobs")
|
|
343
|
+
out(" --embeddings Enable semantic file search for all jobs")
|
|
344
|
+
out(" --max-llm-calls N Cap model↔tool iterations per job")
|
|
345
|
+
out(" --session <uuid> Share session history with a running gemcode session")
|
|
346
|
+
out()
|
|
347
|
+
out("Tools available to jobs (in addition to normal GemCode tools):")
|
|
348
|
+
out(" kairos_sleep_ms(duration_ms) — pause this job without blocking others")
|
|
349
|
+
out(" kairos_enqueue_prompt(prompt, priority, session_id)")
|
|
350
|
+
out(" — the model can queue MORE jobs itself")
|
|
351
|
+
out()
|
|
352
|
+
out("Use cases:")
|
|
353
|
+
out(" - Process N files in parallel: each file → one job")
|
|
354
|
+
out(" - Polling loop: job sleeps, re-enqueues itself with kairos_enqueue_prompt")
|
|
355
|
+
out(" - Bulk code generation, test runs, or research across many documents")
|
|
356
|
+
out(" - Background work while you continue chatting in this session")
|
|
357
|
+
out()
|
|
358
|
+
return ReplSlashResult(skip_model_turn=True)
|
|
359
|
+
|
|
321
360
|
# ── /computer ────────────────────────────────────────────────────────────
|
|
322
361
|
if name in ("computer", "browser"):
|
|
323
362
|
args_s = (sc.args or "").strip().lower()
|
|
@@ -0,0 +1,165 @@
|
|
|
1
|
+
"""
|
|
2
|
+
Tool system prompt manifest.
|
|
3
|
+
|
|
4
|
+
Claude Code's "tool system" approach is largely prompt-driven: it provides the
|
|
5
|
+
model a clear, consistent contract about what tools exist, what categories they
|
|
6
|
+
fall into, and what the model is allowed to do with each tool.
|
|
7
|
+
|
|
8
|
+
GemCode primarily enforces policy via ADK callbacks, but adding a short,
|
|
9
|
+
deterministic tool manifest to the model instruction improves behavior and
|
|
10
|
+
reduces tool-call mistakes.
|
|
11
|
+
|
|
12
|
+
This module builds a compact manifest string from GemCodeConfig.
|
|
13
|
+
"""
|
|
14
|
+
|
|
15
|
+
from __future__ import annotations
|
|
16
|
+
|
|
17
|
+
import os
|
|
18
|
+
from typing import Iterable
|
|
19
|
+
|
|
20
|
+
from gemcode.config import GemCodeConfig
|
|
21
|
+
from gemcode.tool_registry import MUTATING_TOOLS, PLANNING_TOOLS, READ_ONLY_TOOLS, SHELL_TOOLS
|
|
22
|
+
|
|
23
|
+
|
|
24
|
+
def _truthy_env(name: str, *, default: bool = False) -> bool:
|
|
25
|
+
v = os.environ.get(name)
|
|
26
|
+
if v is None:
|
|
27
|
+
return default
|
|
28
|
+
return v.lower() in ("1", "true", "yes", "on")
|
|
29
|
+
|
|
30
|
+
|
|
31
|
+
def _cap(s: str, *, max_chars: int) -> str:
|
|
32
|
+
if len(s) <= max_chars:
|
|
33
|
+
return s
|
|
34
|
+
return s[: max_chars - 3] + "..."
|
|
35
|
+
|
|
36
|
+
|
|
37
|
+
def _fmt_list(items: Iterable[str]) -> str:
|
|
38
|
+
xs = [x for x in items if x]
|
|
39
|
+
xs.sort(key=lambda a: a.lower())
|
|
40
|
+
return ", ".join(xs)
|
|
41
|
+
|
|
42
|
+
|
|
43
|
+
def build_tool_manifest(cfg: GemCodeConfig) -> str | None:
|
|
44
|
+
"""
|
|
45
|
+
Returns a compact "tool system" section to append to the LLM instruction,
|
|
46
|
+
or None if disabled.
|
|
47
|
+
"""
|
|
48
|
+
enabled = _truthy_env("GEMCODE_ENABLE_TOOL_SYSTEM_PROMPT", default=True)
|
|
49
|
+
if not enabled:
|
|
50
|
+
return None
|
|
51
|
+
|
|
52
|
+
max_chars = int(os.environ.get("GEMCODE_TOOL_SYSTEM_PROMPT_MAX_CHARS", "6000"))
|
|
53
|
+
|
|
54
|
+
permission_mode = getattr(cfg, "permission_mode", "default")
|
|
55
|
+
yes_to_all = bool(getattr(cfg, "yes_to_all", False))
|
|
56
|
+
interactive_ask_on = bool(getattr(cfg, "interactive_permission_ask", False))
|
|
57
|
+
sticky_hitl = bool(getattr(cfg, "interactive_hitl_sticky_session", True))
|
|
58
|
+
|
|
59
|
+
# Core custom tools.
|
|
60
|
+
read_only = sorted(READ_ONLY_TOOLS)
|
|
61
|
+
mutating = sorted(MUTATING_TOOLS)
|
|
62
|
+
shell = sorted(SHELL_TOOLS)
|
|
63
|
+
planning = sorted(PLANNING_TOOLS)
|
|
64
|
+
|
|
65
|
+
# Deep research built-ins are not always safe to combine; we only describe
|
|
66
|
+
# what's actually enabled by config.
|
|
67
|
+
deep_research_on = bool(getattr(cfg, "enable_deep_research", False))
|
|
68
|
+
embeddings_on = bool(getattr(cfg, "enable_embeddings", False))
|
|
69
|
+
computer_on = bool(getattr(cfg, "enable_computer_use", False))
|
|
70
|
+
|
|
71
|
+
maps_grounding_on = bool(getattr(cfg, "enable_maps_grounding", False))
|
|
72
|
+
tool_comb_mode = getattr(cfg, "tool_combination_mode", None) or "deep_research"
|
|
73
|
+
|
|
74
|
+
allow_cmds = getattr(cfg, "allow_commands", None)
|
|
75
|
+
allow_cmds_str = ""
|
|
76
|
+
if allow_cmds:
|
|
77
|
+
allow_cmds_str = _fmt_list(list(allow_cmds))
|
|
78
|
+
|
|
79
|
+
# Provide Gemini 3 tool-context circulation contract (best-effort).
|
|
80
|
+
# We can't guarantee exact tool combination semantics, but the model can
|
|
81
|
+
# align expectations with GemCode's behavior.
|
|
82
|
+
gemini3_combination_contract = (
|
|
83
|
+
f"tool-context-circulation mode is {tool_comb_mode}. "
|
|
84
|
+
"When enabled, built-in tool results may appear in context for subsequent function tool calls."
|
|
85
|
+
)
|
|
86
|
+
|
|
87
|
+
mutating_policy_extra = ""
|
|
88
|
+
if interactive_ask_on:
|
|
89
|
+
mutating_policy_extra = (
|
|
90
|
+
" If user_in_run_hitl_prompt_enabled is true, the session will show an inline approval prompt—"
|
|
91
|
+
"do **not** tell the user to re-run with `--yes`; wait for approval and proceed."
|
|
92
|
+
)
|
|
93
|
+
else:
|
|
94
|
+
mutating_policy_extra = (
|
|
95
|
+
" If neither --yes nor inline HITL is available, ask the user to re-run with `--yes` when mutations are required."
|
|
96
|
+
)
|
|
97
|
+
|
|
98
|
+
memory_on = bool(getattr(cfg, "enable_memory", False))
|
|
99
|
+
|
|
100
|
+
manifest = f"""## Tool system (GemCode)
|
|
101
|
+
|
|
102
|
+
### Execution model
|
|
103
|
+
- Issue **multiple independent tool calls in one step** (parallel reads, parallel grep, parallel run_subtask). Use sequential calls only when step B needs step A's result.
|
|
104
|
+
- **Reason end-to-end autonomously.** The user expects complete tasks, not a questionnaire. Use `think` before complex actions, `todo_write` to track multi-step work.
|
|
105
|
+
- **Never stop after the first tool call succeeds.** Keep going until the full task is done or you hit a genuine blocker.
|
|
106
|
+
|
|
107
|
+
### Permission policy
|
|
108
|
+
| Setting | Value |
|
|
109
|
+
|---------|-------|
|
|
110
|
+
| permission_mode | {permission_mode} |
|
|
111
|
+
| --yes (auto-approve mutations) | {yes_to_all} |
|
|
112
|
+
| interactive HITL prompt | {interactive_ask_on} |
|
|
113
|
+
| sticky HITL (approve once → whole session) | {sticky_hitl} |
|
|
114
|
+
|
|
115
|
+
{mutating_policy_extra.strip()}
|
|
116
|
+
|
|
117
|
+
### Planning & reasoning tools: {_fmt_list(planning)}
|
|
118
|
+
- **`todo_write(todos, merge)`** — in-memory task tracker.
|
|
119
|
+
- `todos`: list of objects with `id` (str), `content` (str), `status` (one of: `pending`, `in_progress`, `completed`, `cancelled`).
|
|
120
|
+
- `merge=false` (default): replace the entire list. `merge=true`: upsert by id (update existing, add new, leave others unchanged).
|
|
121
|
+
- Workflow: create with status=pending at task start → update to in_progress when you begin → completed when done.
|
|
122
|
+
- Use for ANY task with 3+ distinct steps.
|
|
123
|
+
- **`think(thought)`** — private reasoning scratchpad. Not shown to user. Use before: complex refactors, debugging hypotheses, destructive actions, architecture decisions.
|
|
124
|
+
- **`run_subtask(task, context)`** — spawn an isolated sub-agent with a fresh context window.
|
|
125
|
+
- Inherits your permissions and tool set (except run_subtask itself — no recursion).
|
|
126
|
+
- Returns the sub-agent's final text as `result`.
|
|
127
|
+
- Use for: context-heavy exploration (reading 50+ files), parallel investigation of independent subsystems, verification passes after you finish work.
|
|
128
|
+
- Always give the sub-agent enough context to work independently; end the task with "Summarise your findings clearly."
|
|
129
|
+
|
|
130
|
+
### Read-only tools (no permission needed): {_fmt_list(read_only)}
|
|
131
|
+
Use proactively. Never use bash/run_command just to list or read files — these are instant and require zero approval.
|
|
132
|
+
|
|
133
|
+
### Mutating tools (WRITE/EDIT/DELETE): {_fmt_list(mutating)}
|
|
134
|
+
Require: `--yes` OR inline HITL approval.
|
|
135
|
+
- `write_file(path, content)` — create or overwrite. Always `read_file` first if the file exists.
|
|
136
|
+
- `search_replace(path, old_string, new_string)` — targeted in-place edit. `old_string` must be unique in the file; include 3+ lines of context.
|
|
137
|
+
- `delete_file(path)`, `move_file(src, dest)` — destructive; think before calling.
|
|
138
|
+
|
|
139
|
+
### Shell tools: {_fmt_list(shell)}
|
|
140
|
+
Require: `--yes` OR inline HITL approval. Allowed commands: {allow_cmds_str or "(default allowlist)"}.
|
|
141
|
+
- `bash(command, timeout_seconds, cwd_subdir, background)` — full shell with pipes, redirects.
|
|
142
|
+
- `background=true` for dev servers, watchers, long builds.
|
|
143
|
+
- Use `2>&1 | tail -N` to cap verbose output.
|
|
144
|
+
- `run_command(command, args, cwd_subdir, background, extra_env_keys, extra_env_values)` — single-executable without shell features.
|
|
145
|
+
- Use `extra_env_keys`/`extra_env_values` for non-interactive environment injection.
|
|
146
|
+
- Prefer `python -m pip` over `pip` to stay in the active virtualenv.
|
|
147
|
+
|
|
148
|
+
### Optional capability tools
|
|
149
|
+
| Capability | Status | Tools available |
|
|
150
|
+
|-----------|--------|----------------|
|
|
151
|
+
| Deep research | {'**ON**' if deep_research_on else 'off'} | {('google_search, url_context' + (', google_maps_grounding' if maps_grounding_on else '')) if deep_research_on else '—'} |
|
|
152
|
+
| Embeddings / semantic search | {'**ON**' if embeddings_on else 'off'} | {'semantic_search_files' if embeddings_on else '—'} |
|
|
153
|
+
| Persistent memory | {'**ON**' if memory_on else 'off'} | {'preload_memory (auto-injected), memory loaded from .gemcode/memories.jsonl' if memory_on else '— (enable with /memory on)'} |
|
|
154
|
+
| Browser / computer use | {'**ON**' if computer_on else 'off'} | {'navigate, click_at, type_text_at, browser_screenshot, browser_find_element, scroll_at, key_combination, ...' if computer_on else '— (enable with /computer on)'} |
|
|
155
|
+
|
|
156
|
+
{gemini3_combination_contract}
|
|
157
|
+
|
|
158
|
+
Enable any capability mid-session with: `/research on`, `/embeddings on`, `/memory on`, `/computer on`.
|
|
159
|
+
|
|
160
|
+
### Policy
|
|
161
|
+
If a tool call is rejected, do NOT retry the same mutation without the required user confirmation. Adjust your plan and report clearly.
|
|
162
|
+
"""
|
|
163
|
+
|
|
164
|
+
return _cap(manifest, max_chars=max_chars)
|
|
165
|
+
|
|
@@ -14,6 +14,22 @@ from gemcode.tools.todo import make_todo_tool
|
|
|
14
14
|
from gemcode.tools.web import make_web_fetch_tool
|
|
15
15
|
|
|
16
16
|
|
|
17
|
+
def _wrap_long_running(fn):
|
|
18
|
+
"""
|
|
19
|
+
Wrap a function tool with ADK's LongRunningFunctionTool so that long-running
|
|
20
|
+
operations (npm install, cargo build, pytest, etc.) can run beyond the normal
|
|
21
|
+
streaming timeout and yield intermediate updates.
|
|
22
|
+
|
|
23
|
+
Falls back gracefully to the plain function if google-adk does not support
|
|
24
|
+
LongRunningFunctionTool in the installed version.
|
|
25
|
+
"""
|
|
26
|
+
try:
|
|
27
|
+
from google.adk.tools import LongRunningFunctionTool
|
|
28
|
+
return LongRunningFunctionTool(fn)
|
|
29
|
+
except Exception:
|
|
30
|
+
return fn
|
|
31
|
+
|
|
32
|
+
|
|
17
33
|
def build_function_tools(cfg: GemCodeConfig, *, include_subtask: bool = True) -> list:
|
|
18
34
|
read_file, list_directory, glob_files, delete_file, move_file = make_filesystem_tools(cfg)
|
|
19
35
|
grep_content = make_grep_tool(cfg)
|
|
@@ -24,6 +40,12 @@ def build_function_tools(cfg: GemCodeConfig, *, include_subtask: bool = True) ->
|
|
|
24
40
|
think = make_think_tool()
|
|
25
41
|
web_fetch = make_web_fetch_tool()
|
|
26
42
|
|
|
43
|
+
# bash and run_command are the most common long-running tools (builds, tests,
|
|
44
|
+
# installs). Wrap them with LongRunningFunctionTool so ADK can handle slow
|
|
45
|
+
# processes without hitting streaming timeouts.
|
|
46
|
+
bash_tool = _wrap_long_running(bash)
|
|
47
|
+
run_command_tool = _wrap_long_running(run_command)
|
|
48
|
+
|
|
27
49
|
tools = [
|
|
28
50
|
todo_write,
|
|
29
51
|
think,
|
|
@@ -31,8 +53,8 @@ def build_function_tools(cfg: GemCodeConfig, *, include_subtask: bool = True) ->
|
|
|
31
53
|
list_directory,
|
|
32
54
|
glob_files,
|
|
33
55
|
grep_content,
|
|
34
|
-
|
|
35
|
-
|
|
56
|
+
bash_tool,
|
|
57
|
+
run_command_tool,
|
|
36
58
|
write_file,
|
|
37
59
|
search_replace,
|
|
38
60
|
move_file,
|
|
@@ -55,6 +55,7 @@ SLASH_COMMANDS: list[tuple[str, str]] = [
|
|
|
55
55
|
("memory", "Toggle persistent memory · /memory on|off"),
|
|
56
56
|
("budget", "Set per-turn token budget · /budget <N> · /budget off"),
|
|
57
57
|
("limits", "Show/set execution limits (max_llm_calls, context, etc.)"),
|
|
58
|
+
("kairos", "Background parallel job scheduler — how to run gemcode kairos"),
|
|
58
59
|
("tools", "List all tools and their permission categories"),
|
|
59
60
|
("config", "Show full active configuration (all fields)"),
|
|
60
61
|
("permissions", "Show current permission mode (default / strict / yes)"),
|
|
@@ -252,15 +252,26 @@ async def run_gemcode_scrollback_tui(
|
|
|
252
252
|
REQUEST_CONFIRMATION_FC = _ADK_REQUEST_CONFIRMATION
|
|
253
253
|
|
|
254
254
|
def _get_confirmation_fcs(events: list) -> list[types.FunctionCall]:
|
|
255
|
-
|
|
256
|
-
|
|
255
|
+
"""Return confirmation FCs from the LAST event that contains them.
|
|
256
|
+
|
|
257
|
+
The ADK runner expects function responses only for the function calls
|
|
258
|
+
in the most recent event. Sending responses for FCs from earlier events
|
|
259
|
+
in the same batch raises:
|
|
260
|
+
ValueError: Last response event should only contain the responses for
|
|
261
|
+
the function calls in the same function call event.
|
|
262
|
+
So we scan backwards and return only the first (= last) match.
|
|
263
|
+
"""
|
|
264
|
+
for ev in reversed(events):
|
|
257
265
|
try:
|
|
258
|
-
|
|
259
|
-
|
|
260
|
-
|
|
266
|
+
fcs = [
|
|
267
|
+
fc for fc in (ev.get_function_calls() or [])
|
|
268
|
+
if getattr(fc, "name", None) == _ADK_REQUEST_CONFIRMATION
|
|
269
|
+
]
|
|
270
|
+
if fcs:
|
|
271
|
+
return fcs
|
|
261
272
|
except Exception:
|
|
262
273
|
continue
|
|
263
|
-
return
|
|
274
|
+
return []
|
|
264
275
|
|
|
265
276
|
def _extract_tool_and_hint(fc: types.FunctionCall) -> tuple[str, str]:
|
|
266
277
|
tool_name = "unknown_tool"
|
|
@@ -527,26 +538,40 @@ async def run_gemcode_scrollback_tui(
|
|
|
527
538
|
# as different phases of the turn complete.
|
|
528
539
|
_start_anim("Thinking\u2026")
|
|
529
540
|
|
|
530
|
-
|
|
531
|
-
|
|
532
|
-
|
|
533
|
-
|
|
534
|
-
|
|
535
|
-
|
|
536
|
-
|
|
537
|
-
if not getattr(ev, "author", None) or ev.author == "user":
|
|
538
|
-
continue
|
|
539
|
-
for part in ev.content.parts:
|
|
540
|
-
delta = getattr(part, "text", None)
|
|
541
|
-
if not delta:
|
|
541
|
+
try:
|
|
542
|
+
async for ev in runner.run_async(**kwargs):
|
|
543
|
+
events.append(ev)
|
|
544
|
+
_render_tool_calls(ev)
|
|
545
|
+
_render_tool_results(ev)
|
|
546
|
+
try:
|
|
547
|
+
if not ev.content or not ev.content.parts:
|
|
542
548
|
continue
|
|
543
|
-
|
|
544
|
-
|
|
545
|
-
|
|
546
|
-
|
|
547
|
-
|
|
548
|
-
|
|
549
|
-
|
|
549
|
+
if not getattr(ev, "author", None) or ev.author == "user":
|
|
550
|
+
continue
|
|
551
|
+
for part in ev.content.parts:
|
|
552
|
+
delta = getattr(part, "text", None)
|
|
553
|
+
if not delta:
|
|
554
|
+
continue
|
|
555
|
+
assistant_wrote_text = True
|
|
556
|
+
if getattr(part, "thought", None):
|
|
557
|
+
buffered_thought.append(delta)
|
|
558
|
+
else:
|
|
559
|
+
buffered_final.append(delta)
|
|
560
|
+
except Exception:
|
|
561
|
+
continue
|
|
562
|
+
except Exception as _turn_err:
|
|
563
|
+
# Catch runner errors (e.g. ADK ValueError from mismatched function
|
|
564
|
+
# response IDs) so a single bad turn doesn't crash the whole TUI.
|
|
565
|
+
_stop_anim()
|
|
566
|
+
print(
|
|
567
|
+
f"\n {ansi.blue_warn}[gemcode] turn error: "
|
|
568
|
+
f"{type(_turn_err).__name__}: {_turn_err}{ansi.reset}"
|
|
569
|
+
)
|
|
570
|
+
print(
|
|
571
|
+
f" {ansi.dim}The agent encountered an internal error on this turn. "
|
|
572
|
+
f"Please send your message again.{ansi.reset}\n"
|
|
573
|
+
)
|
|
574
|
+
break
|
|
550
575
|
|
|
551
576
|
_stop_anim() # ensure spinner is gone before printing the response
|
|
552
577
|
|
|
@@ -1,142 +0,0 @@
|
|
|
1
|
-
"""
|
|
2
|
-
Tool system prompt manifest.
|
|
3
|
-
|
|
4
|
-
Claude Code's "tool system" approach is largely prompt-driven: it provides the
|
|
5
|
-
model a clear, consistent contract about what tools exist, what categories they
|
|
6
|
-
fall into, and what the model is allowed to do with each tool.
|
|
7
|
-
|
|
8
|
-
GemCode primarily enforces policy via ADK callbacks, but adding a short,
|
|
9
|
-
deterministic tool manifest to the model instruction improves behavior and
|
|
10
|
-
reduces tool-call mistakes.
|
|
11
|
-
|
|
12
|
-
This module builds a compact manifest string from GemCodeConfig.
|
|
13
|
-
"""
|
|
14
|
-
|
|
15
|
-
from __future__ import annotations
|
|
16
|
-
|
|
17
|
-
import os
|
|
18
|
-
from typing import Iterable
|
|
19
|
-
|
|
20
|
-
from gemcode.config import GemCodeConfig
|
|
21
|
-
from gemcode.tool_registry import MUTATING_TOOLS, PLANNING_TOOLS, READ_ONLY_TOOLS, SHELL_TOOLS
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
def _truthy_env(name: str, *, default: bool = False) -> bool:
|
|
25
|
-
v = os.environ.get(name)
|
|
26
|
-
if v is None:
|
|
27
|
-
return default
|
|
28
|
-
return v.lower() in ("1", "true", "yes", "on")
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
def _cap(s: str, *, max_chars: int) -> str:
|
|
32
|
-
if len(s) <= max_chars:
|
|
33
|
-
return s
|
|
34
|
-
return s[: max_chars - 3] + "..."
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
def _fmt_list(items: Iterable[str]) -> str:
|
|
38
|
-
xs = [x for x in items if x]
|
|
39
|
-
xs.sort(key=lambda a: a.lower())
|
|
40
|
-
return ", ".join(xs)
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
def build_tool_manifest(cfg: GemCodeConfig) -> str | None:
|
|
44
|
-
"""
|
|
45
|
-
Returns a compact "tool system" section to append to the LLM instruction,
|
|
46
|
-
or None if disabled.
|
|
47
|
-
"""
|
|
48
|
-
enabled = _truthy_env("GEMCODE_ENABLE_TOOL_SYSTEM_PROMPT", default=True)
|
|
49
|
-
if not enabled:
|
|
50
|
-
return None
|
|
51
|
-
|
|
52
|
-
max_chars = int(os.environ.get("GEMCODE_TOOL_SYSTEM_PROMPT_MAX_CHARS", "3200"))
|
|
53
|
-
|
|
54
|
-
permission_mode = getattr(cfg, "permission_mode", "default")
|
|
55
|
-
yes_to_all = bool(getattr(cfg, "yes_to_all", False))
|
|
56
|
-
interactive_ask_on = bool(getattr(cfg, "interactive_permission_ask", False))
|
|
57
|
-
sticky_hitl = bool(getattr(cfg, "interactive_hitl_sticky_session", True))
|
|
58
|
-
|
|
59
|
-
# Core custom tools.
|
|
60
|
-
read_only = sorted(READ_ONLY_TOOLS)
|
|
61
|
-
mutating = sorted(MUTATING_TOOLS)
|
|
62
|
-
shell = sorted(SHELL_TOOLS)
|
|
63
|
-
planning = sorted(PLANNING_TOOLS)
|
|
64
|
-
|
|
65
|
-
# Deep research built-ins are not always safe to combine; we only describe
|
|
66
|
-
# what's actually enabled by config.
|
|
67
|
-
deep_research_on = bool(getattr(cfg, "enable_deep_research", False))
|
|
68
|
-
embeddings_on = bool(getattr(cfg, "enable_embeddings", False))
|
|
69
|
-
computer_on = bool(getattr(cfg, "enable_computer_use", False))
|
|
70
|
-
|
|
71
|
-
maps_grounding_on = bool(getattr(cfg, "enable_maps_grounding", False))
|
|
72
|
-
tool_comb_mode = getattr(cfg, "tool_combination_mode", None) or "deep_research"
|
|
73
|
-
|
|
74
|
-
allow_cmds = getattr(cfg, "allow_commands", None)
|
|
75
|
-
allow_cmds_str = ""
|
|
76
|
-
if allow_cmds:
|
|
77
|
-
allow_cmds_str = _fmt_list(list(allow_cmds))
|
|
78
|
-
|
|
79
|
-
# Provide Gemini 3 tool-context circulation contract (best-effort).
|
|
80
|
-
# We can't guarantee exact tool combination semantics, but the model can
|
|
81
|
-
# align expectations with GemCode's behavior.
|
|
82
|
-
gemini3_combination_contract = (
|
|
83
|
-
f"tool-context-circulation mode is {tool_comb_mode}. "
|
|
84
|
-
"When enabled, built-in tool results may appear in context for subsequent function tool calls."
|
|
85
|
-
)
|
|
86
|
-
|
|
87
|
-
mutating_policy_extra = ""
|
|
88
|
-
if interactive_ask_on:
|
|
89
|
-
mutating_policy_extra = (
|
|
90
|
-
" If user_in_run_hitl_prompt_enabled is true, the session will show an inline approval prompt—"
|
|
91
|
-
"do **not** tell the user to re-run with `--yes`; wait for approval and proceed."
|
|
92
|
-
)
|
|
93
|
-
else:
|
|
94
|
-
mutating_policy_extra = (
|
|
95
|
-
" If neither --yes nor inline HITL is available, ask the user to re-run with `--yes` when mutations are required."
|
|
96
|
-
)
|
|
97
|
-
|
|
98
|
-
manifest = f"""## Tool system (GemCode)
|
|
99
|
-
|
|
100
|
-
Model behavior:
|
|
101
|
-
- Issue **multiple tool calls in one assistant step** when calls are independent (e.g. several `read_file`/`glob_files`/`grep_content` in parallel). Use a single tool call when the next action depends on the previous result.
|
|
102
|
-
- **Reason end-to-end:** you decide which tools to call and in what order—the user expects autonomous execution within policy, not a questionnaire.
|
|
103
|
-
|
|
104
|
-
Permission policy:
|
|
105
|
-
- permission_mode={permission_mode}
|
|
106
|
-
- user_confirmation_provided(--yes)={yes_to_all}
|
|
107
|
-
- user_in_run_hitl_prompt_enabled={interactive_ask_on}
|
|
108
|
-
- session_sticky_hitl={sticky_hitl}: when true, after the user approves **one** tool in this session, further mutating/shell tools may run without re-prompting (set GEMCODE_HITL_STICKY_SESSION=0 to prompt every time).
|
|
109
|
-
|
|
110
|
-
You may call tools as follows:
|
|
111
|
-
- Session planning / reasoning: {_fmt_list(planning)}.
|
|
112
|
-
- `todo_write`: in-memory task list (no disk writes). Use for non-trivial multi-step work; merge=true to upsert by id.
|
|
113
|
-
- `think`: private scratchpad — write reasoning/analysis before risky or complex actions. Not shown to user.
|
|
114
|
-
- `run_subtask(task, context)`: spawn an isolated sub-agent to handle context-heavy work in parallel or in sequence. Sub-agent inherits your permissions and returns its final text as `result`. Exclude run_subtask from sub-agent by design (no recursion).
|
|
115
|
-
- Read-only tools: {_fmt_list(read_only)}.
|
|
116
|
-
Use these proactively to locate files and code before asking the user for paths.
|
|
117
|
-
- Mutating tools (WRITE/EDIT/DELETE): {_fmt_list(mutating)}.
|
|
118
|
-
Only call if user_confirmation_provided(--yes) is true OR user_in_run_hitl_prompt_enabled is true.{mutating_policy_extra}
|
|
119
|
-
- Shell tool (run_command): {_fmt_list(shell)}.
|
|
120
|
-
Prefer allowlisted commands (GEMCODE_ALLOW_COMMANDS). Allowed={allow_cmds_str or "<none>"}.
|
|
121
|
-
If the user approves a run_command in the session prompt, that specific invocation may run even when the executable is not on the default list.
|
|
122
|
-
Only call if user_confirmation_provided(--yes) is true OR user_in_run_hitl_prompt_enabled is true.
|
|
123
|
-
Notes:
|
|
124
|
-
- Prefer `python -m pip ...` (or `python3 -m pip ...`) so installs stay in the active virtualenv.
|
|
125
|
-
- Do not assume sudo/system package manager access.
|
|
126
|
-
- `run_command` supports `cwd_subdir` (relative path under the project) instead of `cd`/`bash`; use parallel `extra_env_keys` / `extra_env_values` (e.g. ["CI"] and ["1"]) for non-interactive installers; use `background=true` for long-running dev servers.
|
|
127
|
-
|
|
128
|
-
Optional capability tools:
|
|
129
|
-
- Deep research built-ins are {'ON' if deep_research_on else 'OFF'}.
|
|
130
|
-
Active built-ins: google_search, url_context{', google_maps' if deep_research_on and maps_grounding_on else ''}.
|
|
131
|
-
{gemini3_combination_contract}
|
|
132
|
-
- Embeddings semantic retrieval is {'ON' if embeddings_on else 'OFF'}:
|
|
133
|
-
semantic_search_files.
|
|
134
|
-
- Computer use is {'ON' if computer_on else 'OFF'}:
|
|
135
|
-
browser automation actions via Computer Use toolset.
|
|
136
|
-
Only call if permission_mode != strict AND (user_confirmation_provided(--yes) is true OR user_in_run_hitl_prompt_enabled is true).
|
|
137
|
-
|
|
138
|
-
If a tool call is rejected by policy, do NOT retry the same mutation without the required user confirmation.
|
|
139
|
-
"""
|
|
140
|
-
|
|
141
|
-
return _cap(manifest, max_chars=max_chars)
|
|
142
|
-
|
|
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
|