krnl-code 1.0.4__py3-none-any.whl
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.
- krnl_agent/__init__.py +9 -0
- krnl_agent/__main__.py +7 -0
- krnl_agent/agent_registry.py +95 -0
- krnl_agent/agent_selector.py +69 -0
- krnl_agent/audit_log.py +155 -0
- krnl_agent/background.py +94 -0
- krnl_agent/checkpoints.py +67 -0
- krnl_agent/ci.py +73 -0
- krnl_agent/cli.py +1458 -0
- krnl_agent/commands.py +42 -0
- krnl_agent/config.py +425 -0
- krnl_agent/context.py +352 -0
- krnl_agent/depaudit.py +63 -0
- krnl_agent/deploy.py +245 -0
- krnl_agent/doctor.py +106 -0
- krnl_agent/events.py +141 -0
- krnl_agent/gitignore.py +47 -0
- krnl_agent/graph.py +928 -0
- krnl_agent/guardrails.py +70 -0
- krnl_agent/headless.py +60 -0
- krnl_agent/history.py +49 -0
- krnl_agent/hooks.py +72 -0
- krnl_agent/ingest.py +129 -0
- krnl_agent/llm.py +456 -0
- krnl_agent/loop.py +779 -0
- krnl_agent/mcp_client.py +128 -0
- krnl_agent/memory.py +61 -0
- krnl_agent/modelrouter.py +151 -0
- krnl_agent/monitor.py +112 -0
- krnl_agent/notify.py +119 -0
- krnl_agent/parallel_executor.py +139 -0
- krnl_agent/permissions.py +128 -0
- krnl_agent/plugins.py +105 -0
- krnl_agent/pricing.py +85 -0
- krnl_agent/prompts.py +60 -0
- krnl_agent/repomap.py +133 -0
- krnl_agent/sandbox.py +69 -0
- krnl_agent/scaffold.py +167 -0
- krnl_agent/schedules.py +137 -0
- krnl_agent/secrets.py +100 -0
- krnl_agent/selfheal.py +87 -0
- krnl_agent/server.py +302 -0
- krnl_agent/sessions.py +258 -0
- krnl_agent/settings.py +59 -0
- krnl_agent/skills.py +73 -0
- krnl_agent/teams.py +38 -0
- krnl_agent/tool_schemas.py +431 -0
- krnl_agent/tools.py +694 -0
- krnl_agent/webtools.py +139 -0
- krnl_code-1.0.4.dist-info/METADATA +214 -0
- krnl_code-1.0.4.dist-info/RECORD +56 -0
- krnl_code-1.0.4.dist-info/WHEEL +5 -0
- krnl_code-1.0.4.dist-info/entry_points.txt +2 -0
- krnl_code-1.0.4.dist-info/licenses/LICENSE +147 -0
- krnl_code-1.0.4.dist-info/licenses/NOTICE +4 -0
- krnl_code-1.0.4.dist-info/top_level.txt +1 -0
krnl_agent/teams.py
ADDED
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
"""Multi-agent teams — a coordinator that delegates to specialist sub-agents,
|
|
2
|
+
with team state that persists across sessions.
|
|
3
|
+
|
|
4
|
+
A team is a named, persistent coordinator session (`team-<name>`). The
|
|
5
|
+
coordinator breaks work into subtasks and delegates each to a specialist via
|
|
6
|
+
`spawn_agent`; progress is tracked with `todo_write`. Because the team is a
|
|
7
|
+
persisted session, you can stop and resume later:
|
|
8
|
+
|
|
9
|
+
krnl-agent --team-name auth-sprint "Plan and implement auth with tests"
|
|
10
|
+
krnl-agent --team-name auth-sprint # resume the team
|
|
11
|
+
"""
|
|
12
|
+
from __future__ import annotations
|
|
13
|
+
|
|
14
|
+
import re
|
|
15
|
+
|
|
16
|
+
from . import sessions
|
|
17
|
+
|
|
18
|
+
TEAM_PREFIX = "team-"
|
|
19
|
+
|
|
20
|
+
|
|
21
|
+
def team_id(name: str) -> str:
|
|
22
|
+
slug = re.sub(r"[^a-zA-Z0-9_-]+", "-", name).strip("-").lower() or "team"
|
|
23
|
+
return TEAM_PREFIX + slug
|
|
24
|
+
|
|
25
|
+
|
|
26
|
+
def list_teams() -> list[dict]:
|
|
27
|
+
return [s for s in sessions.list_sessions() if str(s.get("id", "")).startswith(TEAM_PREFIX)]
|
|
28
|
+
|
|
29
|
+
|
|
30
|
+
def coordinator_prompt(name: str) -> str:
|
|
31
|
+
return (
|
|
32
|
+
f"\n\n# Team mode — you are the COORDINATOR of team '{name}'.\n"
|
|
33
|
+
"Plan the work, then DELEGATE each independent subtask to a specialist via "
|
|
34
|
+
"`spawn_agent` (give each a focused role and a complete prompt). Track the "
|
|
35
|
+
"plan with `todo_write` (one item in_progress at a time). Run specialists in "
|
|
36
|
+
"parallel where possible, integrate their results, and verify the whole. "
|
|
37
|
+
"Team state persists, so summarize progress clearly for future sessions."
|
|
38
|
+
)
|
|
@@ -0,0 +1,431 @@
|
|
|
1
|
+
"""OpenAI-style tool (function) schemas exposed to the model.
|
|
2
|
+
|
|
3
|
+
These map 1:1 to the implementations in `tools.py`. Tools whose name appears in
|
|
4
|
+
`MUTATING_TOOLS` go through the approval gate before they execute.
|
|
5
|
+
"""
|
|
6
|
+
from __future__ import annotations
|
|
7
|
+
|
|
8
|
+
MUTATING_TOOLS = {
|
|
9
|
+
"write_file",
|
|
10
|
+
"edit_file",
|
|
11
|
+
"multi_edit",
|
|
12
|
+
"create_file",
|
|
13
|
+
"delete_file",
|
|
14
|
+
"run_command",
|
|
15
|
+
"bash_background",
|
|
16
|
+
"git_commit",
|
|
17
|
+
"worktree_add",
|
|
18
|
+
"worktree_remove",
|
|
19
|
+
"git_branch",
|
|
20
|
+
"git_push",
|
|
21
|
+
"open_pr",
|
|
22
|
+
"deploy",
|
|
23
|
+
"rollback",
|
|
24
|
+
"provision_db",
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
|
|
28
|
+
def _fn(name: str, description: str, properties: dict, required: list[str]) -> dict:
|
|
29
|
+
return {
|
|
30
|
+
"type": "function",
|
|
31
|
+
"function": {
|
|
32
|
+
"name": name,
|
|
33
|
+
"description": description,
|
|
34
|
+
"parameters": {
|
|
35
|
+
"type": "object",
|
|
36
|
+
"properties": properties,
|
|
37
|
+
"required": required,
|
|
38
|
+
},
|
|
39
|
+
},
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
|
|
43
|
+
TOOL_SCHEMAS = [
|
|
44
|
+
_fn(
|
|
45
|
+
"list_files",
|
|
46
|
+
"List files in the workspace (recursive, ignores configured paths). "
|
|
47
|
+
"Use to understand project structure.",
|
|
48
|
+
{
|
|
49
|
+
"path": {
|
|
50
|
+
"type": "string",
|
|
51
|
+
"description": "Directory relative to workspace root. Default '.'.",
|
|
52
|
+
}
|
|
53
|
+
},
|
|
54
|
+
[],
|
|
55
|
+
),
|
|
56
|
+
_fn(
|
|
57
|
+
"read_file",
|
|
58
|
+
"Read a UTF-8 text file from the workspace. Optionally a line range.",
|
|
59
|
+
{
|
|
60
|
+
"path": {"type": "string", "description": "File path relative to root."},
|
|
61
|
+
"start_line": {"type": "integer", "description": "1-based start (optional)."},
|
|
62
|
+
"end_line": {"type": "integer", "description": "Inclusive end (optional)."},
|
|
63
|
+
},
|
|
64
|
+
["path"],
|
|
65
|
+
),
|
|
66
|
+
_fn(
|
|
67
|
+
"search_text",
|
|
68
|
+
"Search file contents across the workspace (ripgrep if available, else "
|
|
69
|
+
"a pure-Python fallback). Returns matching path:line: text rows.",
|
|
70
|
+
{
|
|
71
|
+
"query": {"type": "string", "description": "Substring or regex to find."},
|
|
72
|
+
"glob": {
|
|
73
|
+
"type": "string",
|
|
74
|
+
"description": "Optional file glob filter, e.g. '*.py'.",
|
|
75
|
+
},
|
|
76
|
+
},
|
|
77
|
+
["query"],
|
|
78
|
+
),
|
|
79
|
+
_fn(
|
|
80
|
+
"write_file",
|
|
81
|
+
"Create a new file or fully overwrite an existing one with `content`. "
|
|
82
|
+
"Requires user approval. Prefer edit_file for changing existing files.",
|
|
83
|
+
{
|
|
84
|
+
"path": {"type": "string"},
|
|
85
|
+
"content": {"type": "string", "description": "Full file contents."},
|
|
86
|
+
},
|
|
87
|
+
["path", "content"],
|
|
88
|
+
),
|
|
89
|
+
_fn(
|
|
90
|
+
"edit_file",
|
|
91
|
+
"Make a surgical edit by replacing an exact substring. `old_string` must "
|
|
92
|
+
"match the current file EXACTLY (incl. whitespace) and uniquely. Requires "
|
|
93
|
+
"user approval.",
|
|
94
|
+
{
|
|
95
|
+
"path": {"type": "string"},
|
|
96
|
+
"old_string": {"type": "string", "description": "Exact text to replace."},
|
|
97
|
+
"new_string": {"type": "string", "description": "Replacement text."},
|
|
98
|
+
"replace_all": {
|
|
99
|
+
"type": "boolean",
|
|
100
|
+
"description": "Replace every occurrence (default false).",
|
|
101
|
+
},
|
|
102
|
+
},
|
|
103
|
+
["path", "old_string", "new_string"],
|
|
104
|
+
),
|
|
105
|
+
_fn(
|
|
106
|
+
"create_file",
|
|
107
|
+
"Create a new file with `content`. Fails if it already exists. Requires "
|
|
108
|
+
"user approval.",
|
|
109
|
+
{"path": {"type": "string"}, "content": {"type": "string"}},
|
|
110
|
+
["path", "content"],
|
|
111
|
+
),
|
|
112
|
+
_fn(
|
|
113
|
+
"delete_file",
|
|
114
|
+
"Delete a file from the workspace. Requires user approval.",
|
|
115
|
+
{"path": {"type": "string"}},
|
|
116
|
+
["path"],
|
|
117
|
+
),
|
|
118
|
+
_fn(
|
|
119
|
+
"run_command",
|
|
120
|
+
"Run a shell command in the workspace root (e.g. run tests, a script, a "
|
|
121
|
+
"build). Requires user approval. Has a timeout; not for long-running "
|
|
122
|
+
"servers — use bash_background for those.",
|
|
123
|
+
{"command": {"type": "string", "description": "The shell command to run."}},
|
|
124
|
+
["command"],
|
|
125
|
+
),
|
|
126
|
+
_fn(
|
|
127
|
+
"glob",
|
|
128
|
+
"Find files by glob pattern (supports ** for recursion), e.g. 'src/**/*.ts'.",
|
|
129
|
+
{
|
|
130
|
+
"pattern": {"type": "string", "description": "Glob pattern."},
|
|
131
|
+
"path": {"type": "string", "description": "Base dir relative to root (default '.')."},
|
|
132
|
+
},
|
|
133
|
+
["pattern"],
|
|
134
|
+
),
|
|
135
|
+
_fn(
|
|
136
|
+
"repo_map",
|
|
137
|
+
"Get a compact symbol/outline map of the codebase (per-file classes, "
|
|
138
|
+
"functions, methods with line numbers). Read this FIRST to locate code "
|
|
139
|
+
"cheaply, then read_file only the ranges you need — saves tokens vs reading "
|
|
140
|
+
"whole files.",
|
|
141
|
+
{
|
|
142
|
+
"path": {"type": "string", "description": "Sub-path to map (default '.')."},
|
|
143
|
+
"lang": {"type": "string", "description": "Optional language filter: py|ts|js|go|rs|java|rb."},
|
|
144
|
+
},
|
|
145
|
+
[],
|
|
146
|
+
),
|
|
147
|
+
_fn(
|
|
148
|
+
"secret_scan",
|
|
149
|
+
"Scan the workspace for hard-coded secrets/credentials (API keys, tokens, "
|
|
150
|
+
"private keys, connection strings). Returns masked path:line findings.",
|
|
151
|
+
{"path": {"type": "string", "description": "Sub-path to scan (default '.')."}},
|
|
152
|
+
[],
|
|
153
|
+
),
|
|
154
|
+
_fn(
|
|
155
|
+
"dependency_audit",
|
|
156
|
+
"Audit project dependencies for known vulnerabilities using the ecosystem's "
|
|
157
|
+
"tool (pip-audit / npm audit / cargo audit / govulncheck) when installed.",
|
|
158
|
+
{},
|
|
159
|
+
[],
|
|
160
|
+
),
|
|
161
|
+
_fn(
|
|
162
|
+
"deploy_check",
|
|
163
|
+
"List deployment targets (Cloud Run, Cloudflare, Vercel, Netlify, Fly, "
|
|
164
|
+
"Railway, Render, Docker, Kubernetes/Helm, AWS, Azure, + Neon/Supabase DBs) "
|
|
165
|
+
"and which are READY (CLI installed + credential env var set). Call this "
|
|
166
|
+
"FIRST before deploying to pick a target.",
|
|
167
|
+
{},
|
|
168
|
+
[],
|
|
169
|
+
),
|
|
170
|
+
_fn(
|
|
171
|
+
"deploy",
|
|
172
|
+
"Deploy the current project to a target (e.g. 'cloudrun', 'cloudflare', "
|
|
173
|
+
"'vercel', 'fly', 'render'). Free-tier targets run directly; billable targets "
|
|
174
|
+
"are gated by the spend policy and need approval. `params` fills command "
|
|
175
|
+
"placeholders like {service},{region},{app},{dist}. Requires approval.",
|
|
176
|
+
{
|
|
177
|
+
"target": {"type": "string", "description": "Target name from deploy_check."},
|
|
178
|
+
"params": {"type": "object", "description": "Placeholder values for the deploy command."},
|
|
179
|
+
},
|
|
180
|
+
["target"],
|
|
181
|
+
),
|
|
182
|
+
_fn(
|
|
183
|
+
"rollback",
|
|
184
|
+
"Roll a deployment target back to its previous known-good release "
|
|
185
|
+
"(safe — only reverts state). Requires approval.",
|
|
186
|
+
{
|
|
187
|
+
"target": {"type": "string"},
|
|
188
|
+
"params": {"type": "object", "description": "e.g. {revision},{version},{deployment}."},
|
|
189
|
+
},
|
|
190
|
+
["target"],
|
|
191
|
+
),
|
|
192
|
+
_fn(
|
|
193
|
+
"provision_db",
|
|
194
|
+
"Provision a managed database before deploying the app: 'neon' (serverless "
|
|
195
|
+
"Postgres) or 'supabase' (full backend). Requires approval.",
|
|
196
|
+
{
|
|
197
|
+
"provider": {"type": "string", "description": "'neon' or 'supabase'."},
|
|
198
|
+
"params": {"type": "object"},
|
|
199
|
+
},
|
|
200
|
+
["provider"],
|
|
201
|
+
),
|
|
202
|
+
_fn(
|
|
203
|
+
"monitor_status",
|
|
204
|
+
"Report configured monitoring providers (Sentry/OpenTelemetry/Datadog/uptime) "
|
|
205
|
+
"and current unresolved error / uptime status for the deployed app.",
|
|
206
|
+
{},
|
|
207
|
+
[],
|
|
208
|
+
),
|
|
209
|
+
_fn(
|
|
210
|
+
"health_check",
|
|
211
|
+
"Check a deployed app's health endpoint by URL. Returns healthy/unhealthy.",
|
|
212
|
+
{
|
|
213
|
+
"url": {"type": "string", "description": "Base URL of the deployed app."},
|
|
214
|
+
"path": {"type": "string", "description": "Health path (default /health)."},
|
|
215
|
+
},
|
|
216
|
+
["url"],
|
|
217
|
+
),
|
|
218
|
+
_fn(
|
|
219
|
+
"multi_edit",
|
|
220
|
+
"Apply several exact search/replace edits to ONE file atomically (all or "
|
|
221
|
+
"nothing). Each edit needs old_string (exact, unique unless replace_all) "
|
|
222
|
+
"and new_string. Requires approval.",
|
|
223
|
+
{
|
|
224
|
+
"path": {"type": "string"},
|
|
225
|
+
"edits": {
|
|
226
|
+
"type": "array",
|
|
227
|
+
"description": "List of edits applied in order.",
|
|
228
|
+
"items": {
|
|
229
|
+
"type": "object",
|
|
230
|
+
"properties": {
|
|
231
|
+
"old_string": {"type": "string"},
|
|
232
|
+
"new_string": {"type": "string"},
|
|
233
|
+
"replace_all": {"type": "boolean"},
|
|
234
|
+
},
|
|
235
|
+
"required": ["old_string", "new_string"],
|
|
236
|
+
},
|
|
237
|
+
},
|
|
238
|
+
},
|
|
239
|
+
["path", "edits"],
|
|
240
|
+
),
|
|
241
|
+
_fn(
|
|
242
|
+
"web_search",
|
|
243
|
+
"Search the web for up-to-date information. Returns titles, URLs, snippets.",
|
|
244
|
+
{"query": {"type": "string"}},
|
|
245
|
+
["query"],
|
|
246
|
+
),
|
|
247
|
+
_fn(
|
|
248
|
+
"web_fetch",
|
|
249
|
+
"Fetch a URL and return its readable text content.",
|
|
250
|
+
{"url": {"type": "string"}},
|
|
251
|
+
["url"],
|
|
252
|
+
),
|
|
253
|
+
_fn(
|
|
254
|
+
"bash_background",
|
|
255
|
+
"Start a long-running command (dev server, watcher) in the background and "
|
|
256
|
+
"return immediately with a process id. Requires approval. Poll with "
|
|
257
|
+
"process_output, stop with process_kill.",
|
|
258
|
+
{"command": {"type": "string"}},
|
|
259
|
+
["command"],
|
|
260
|
+
),
|
|
261
|
+
_fn(
|
|
262
|
+
"process_output",
|
|
263
|
+
"Read recent output from a background process started with bash_background.",
|
|
264
|
+
{"id": {"type": "string"}, "tail": {"type": "integer", "description": "Lines (default 100)."}},
|
|
265
|
+
["id"],
|
|
266
|
+
),
|
|
267
|
+
_fn(
|
|
268
|
+
"process_kill",
|
|
269
|
+
"Stop a background process by id.",
|
|
270
|
+
{"id": {"type": "string"}},
|
|
271
|
+
["id"],
|
|
272
|
+
),
|
|
273
|
+
_fn("process_list", "List background processes and their status.", {}, []),
|
|
274
|
+
_fn("git_status", "Show 'git status --short --branch' for the workspace.", {}, []),
|
|
275
|
+
_fn(
|
|
276
|
+
"git_diff",
|
|
277
|
+
"Show 'git diff', optionally for one path.",
|
|
278
|
+
{"path": {"type": "string", "description": "Optional path to diff."}},
|
|
279
|
+
[],
|
|
280
|
+
),
|
|
281
|
+
_fn(
|
|
282
|
+
"git_commit",
|
|
283
|
+
"Stage all changes and create a git commit. Requires approval.",
|
|
284
|
+
{
|
|
285
|
+
"message": {"type": "string", "description": "Commit message."},
|
|
286
|
+
"all": {"type": "boolean", "description": "git add -A first (default true)."},
|
|
287
|
+
},
|
|
288
|
+
["message"],
|
|
289
|
+
),
|
|
290
|
+
_fn(
|
|
291
|
+
"todo_write",
|
|
292
|
+
"Create or update the visible task checklist for a multi-step job. Pass the "
|
|
293
|
+
"FULL list each time. Mark exactly one item in_progress while working it.",
|
|
294
|
+
{
|
|
295
|
+
"todos": {
|
|
296
|
+
"type": "array",
|
|
297
|
+
"items": {
|
|
298
|
+
"type": "object",
|
|
299
|
+
"properties": {
|
|
300
|
+
"content": {"type": "string"},
|
|
301
|
+
"status": {
|
|
302
|
+
"type": "string",
|
|
303
|
+
"enum": ["pending", "in_progress", "completed"],
|
|
304
|
+
},
|
|
305
|
+
},
|
|
306
|
+
"required": ["content", "status"],
|
|
307
|
+
},
|
|
308
|
+
}
|
|
309
|
+
},
|
|
310
|
+
["todos"],
|
|
311
|
+
),
|
|
312
|
+
_fn(
|
|
313
|
+
"spawn_agent",
|
|
314
|
+
"Delegate a focused sub-task to a fresh sub-agent with its own context. Use "
|
|
315
|
+
"for independent investigations or parallelizable work. Returns the "
|
|
316
|
+
"sub-agent's final summary. The sub-agent can read/search/edit/run like you.",
|
|
317
|
+
{
|
|
318
|
+
"description": {"type": "string", "description": "Short label for the sub-task."},
|
|
319
|
+
"prompt": {"type": "string", "description": "Full instructions for the sub-agent."},
|
|
320
|
+
},
|
|
321
|
+
["description", "prompt"],
|
|
322
|
+
),
|
|
323
|
+
_fn(
|
|
324
|
+
"use_skill",
|
|
325
|
+
"Load the full instructions for one of the available SKILLS by name (see the "
|
|
326
|
+
"skills list in your system prompt). Call this before doing the skill's task.",
|
|
327
|
+
{"name": {"type": "string", "description": "The skill name to load."}},
|
|
328
|
+
["name"],
|
|
329
|
+
),
|
|
330
|
+
_fn(
|
|
331
|
+
"worktree_add",
|
|
332
|
+
"Create an isolated git worktree (a separate working copy) to make changes "
|
|
333
|
+
"safely without touching the main tree. Requires approval.",
|
|
334
|
+
{
|
|
335
|
+
"name": {"type": "string", "description": "Worktree name."},
|
|
336
|
+
"branch": {"type": "string", "description": "Optional new branch name."},
|
|
337
|
+
},
|
|
338
|
+
["name"],
|
|
339
|
+
),
|
|
340
|
+
_fn("worktree_list", "List git worktrees.", {}, []),
|
|
341
|
+
_fn(
|
|
342
|
+
"worktree_remove",
|
|
343
|
+
"Remove a git worktree created with worktree_add. Requires approval.",
|
|
344
|
+
{"name": {"type": "string"}},
|
|
345
|
+
["name"],
|
|
346
|
+
),
|
|
347
|
+
_fn(
|
|
348
|
+
"git_branch",
|
|
349
|
+
"Create and switch to a new git branch. Requires approval.",
|
|
350
|
+
{"name": {"type": "string"}},
|
|
351
|
+
["name"],
|
|
352
|
+
),
|
|
353
|
+
_fn(
|
|
354
|
+
"git_push",
|
|
355
|
+
"Push the current branch to origin (sets upstream). Requires approval.",
|
|
356
|
+
{"branch": {"type": "string", "description": "Branch (default current HEAD)."}},
|
|
357
|
+
[],
|
|
358
|
+
),
|
|
359
|
+
_fn(
|
|
360
|
+
"open_pr",
|
|
361
|
+
"Open a GitHub pull request for the current branch via the gh CLI. Requires approval.",
|
|
362
|
+
{"title": {"type": "string"}, "body": {"type": "string"}},
|
|
363
|
+
["title"],
|
|
364
|
+
),
|
|
365
|
+
_fn(
|
|
366
|
+
"memory_write",
|
|
367
|
+
"Record a persistent fact, decision, error, or solution in the session memory. "
|
|
368
|
+
"Use this to remember key architectural decisions, facts, or resolutions.",
|
|
369
|
+
{
|
|
370
|
+
"type": {
|
|
371
|
+
"type": "string",
|
|
372
|
+
"enum": ["fact", "decision", "error", "solution"],
|
|
373
|
+
"description": "The type of memory entry.",
|
|
374
|
+
},
|
|
375
|
+
"content": {
|
|
376
|
+
"type": "string",
|
|
377
|
+
"description": "The detailed fact/decision/error/solution content.",
|
|
378
|
+
},
|
|
379
|
+
"related_files": {
|
|
380
|
+
"type": "array",
|
|
381
|
+
"items": {"type": "string"},
|
|
382
|
+
"description": "List of related files (relative to workspace root).",
|
|
383
|
+
},
|
|
384
|
+
},
|
|
385
|
+
["type", "content"],
|
|
386
|
+
),
|
|
387
|
+
]
|
|
388
|
+
|
|
389
|
+
# Tools handled directly by the loop (not in tools.TOOL_FUNCS).
|
|
390
|
+
LOOP_TOOLS = {"todo_write", "spawn_agent", "update_plan", "use_skill", "memory_write"}
|
|
391
|
+
|
|
392
|
+
# In plan mode only these (read-only + planning) are offered.
|
|
393
|
+
PLAN_MODE_TOOLS = {
|
|
394
|
+
"list_files",
|
|
395
|
+
"read_file",
|
|
396
|
+
"search_text",
|
|
397
|
+
"glob",
|
|
398
|
+
"repo_map",
|
|
399
|
+
"secret_scan",
|
|
400
|
+
"dependency_audit",
|
|
401
|
+
"deploy_check",
|
|
402
|
+
"monitor_status",
|
|
403
|
+
"health_check",
|
|
404
|
+
"web_search",
|
|
405
|
+
"web_fetch",
|
|
406
|
+
"process_list",
|
|
407
|
+
"git_status",
|
|
408
|
+
"git_diff",
|
|
409
|
+
"update_plan",
|
|
410
|
+
"todo_write",
|
|
411
|
+
"use_skill",
|
|
412
|
+
"memory_write",
|
|
413
|
+
}
|
|
414
|
+
|
|
415
|
+
|
|
416
|
+
def schemas_for(plan_mode: bool = False, extra: list | None = None) -> list:
|
|
417
|
+
"""Return the tool schemas to advertise this turn."""
|
|
418
|
+
base = TOOL_SCHEMAS
|
|
419
|
+
if plan_mode:
|
|
420
|
+
base = [t for t in base if t["function"]["name"] in PLAN_MODE_TOOLS]
|
|
421
|
+
base = base + [_PLAN_SCHEMA]
|
|
422
|
+
return base + (extra or [])
|
|
423
|
+
|
|
424
|
+
|
|
425
|
+
_PLAN_SCHEMA = _fn(
|
|
426
|
+
"update_plan",
|
|
427
|
+
"Present your implementation plan for the user to approve before you make any "
|
|
428
|
+
"changes. Call this when you've finished researching in plan mode.",
|
|
429
|
+
{"plan": {"type": "string", "description": "The plan as markdown."}},
|
|
430
|
+
["plan"],
|
|
431
|
+
)
|