agent-devkit 0.3.0 → 0.3.2

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.
@@ -14,6 +14,14 @@ from pathlib import Path
14
14
  from typing import Any
15
15
 
16
16
  from cli.aikit.app_home import app_home, config_path as app_config_path, ensure_app_home
17
+ from cli.aikit.embedded_mini_brain import (
18
+ EMBEDDED_BACKEND_ID,
19
+ EMBEDDED_MODEL_ID,
20
+ EmbeddedMiniBrainError,
21
+ embedded_backend_config,
22
+ embedded_backend_doctor,
23
+ invoke_embedded_mini_brain,
24
+ )
17
25
  from cli.aikit.identity import host_cli_prompt, identity_system_prompt
18
26
 
19
27
 
@@ -33,6 +41,14 @@ class LlmBackend:
33
41
 
34
42
 
35
43
  BACKENDS: dict[str, LlmBackend] = {
44
+ EMBEDDED_BACKEND_ID: LlmBackend(
45
+ id=EMBEDDED_BACKEND_ID,
46
+ display_name="Embedded mini-brain",
47
+ kind="embedded-local",
48
+ auth="none",
49
+ default_model=EMBEDDED_MODEL_ID,
50
+ notes="Uses the Agent DevKit embedded mini-brain for setup, onboarding and low-risk conversation.",
51
+ ),
36
52
  "openai": LlmBackend(
37
53
  id="openai",
38
54
  display_name="OpenAI API",
@@ -100,7 +116,7 @@ BACKENDS: dict[str, LlmBackend] = {
100
116
 
101
117
  ENV_VAR_NAME_PATTERN = re.compile(r"^[A-Za-z_][A-Za-z0-9_]*$")
102
118
  DEFAULT_AGENT_TIMEOUT_SECONDS = 120
103
- DEFAULT_FALLBACK_ORDER = ("claude-code", "codex-cli", "openai", "anthropic", "openrouter", "ollama")
119
+ DEFAULT_FALLBACK_ORDER = ("claude-code", "codex-cli", "openai", "anthropic", "openrouter", "ollama", EMBEDDED_BACKEND_ID)
104
120
 
105
121
 
106
122
  def config_home() -> Path:
@@ -323,6 +339,8 @@ def normalize_backend_order(order: str | list[str] | tuple[str, ...]) -> list[st
323
339
 
324
340
 
325
341
  def default_backend_config(backend: LlmBackend) -> dict[str, Any]:
342
+ if backend.id == EMBEDDED_BACKEND_ID:
343
+ return embedded_backend_config()
326
344
  entry: dict[str, Any] = {"kind": backend.kind, "auth": backend.auth}
327
345
  if backend.auth == "api-key-env":
328
346
  entry["api_key_ref"] = f"env:{backend.api_key_env}"
@@ -346,7 +364,8 @@ def doctor_backends(backend_id: str | None = None) -> dict[str, Any]:
346
364
 
347
365
  checks = [doctor_backend(BACKENDS[item], config) for item in ids]
348
366
  status = "ok"
349
- if any(item["status"] == "missing" for item in checks):
367
+ missing_statuses = {"missing", "not-installed", "dependency-missing", "invalid-model"}
368
+ if any(item["status"] in missing_statuses for item in checks):
350
369
  status = "partial" if not backend_id else "missing"
351
370
  if any(item["status"] == "error" for item in checks):
352
371
  status = "error"
@@ -361,6 +380,8 @@ def doctor_backends(backend_id: str | None = None) -> dict[str, Any]:
361
380
 
362
381
 
363
382
  def doctor_backend(backend: LlmBackend, config: dict[str, Any]) -> dict[str, Any]:
383
+ if backend.id == EMBEDDED_BACKEND_ID:
384
+ return embedded_backend_doctor()
364
385
  configured = config.get("llm", {}).get("backends", {}).get(backend.id, {})
365
386
  if not isinstance(configured, dict):
366
387
  configured = {}
@@ -616,6 +637,11 @@ class LlmPolicyError(LlmInvocationError):
616
637
  def invoke_resolved_backend(backend: dict[str, Any], prompt: str, *, public_name: str = "Agent DevKit") -> str:
617
638
  kind = backend.get("kind")
618
639
  backend_id = backend.get("id")
640
+ if kind == "embedded-local" and backend_id == EMBEDDED_BACKEND_ID:
641
+ try:
642
+ return invoke_embedded_mini_brain(prompt, public_name=public_name)
643
+ except EmbeddedMiniBrainError as exc:
644
+ raise LlmInvocationError(str(exc)) from exc
619
645
  if kind == "openai-compatible":
620
646
  return invoke_openai_compatible(backend, prompt, public_name=public_name)
621
647
  if kind == "anthropic":
@@ -6,6 +6,7 @@ import shutil
6
6
  import subprocess
7
7
  from typing import Any
8
8
 
9
+ from cli.aikit.embedded_mini_brain import EMBEDDED_BACKEND_ID, EMBEDDED_MODEL_ID, embedded_mini_brain_status
9
10
  from cli.aikit.mini_brain import DEFAULT_OLLAMA_MODEL, mini_brain_contract
10
11
  from cli.aikit.model_router import build_model_plan
11
12
  from cli.aikit.ollama import ollama_models, ollama_pull, ollama_status
@@ -28,7 +29,9 @@ def local_llm_list() -> dict[str, Any]:
28
29
  "kind": "local-llm",
29
30
  "schema_version": LOCAL_LLM_SCHEMA_VERSION,
30
31
  "status": "ok",
31
- "provider": "ollama",
32
+ "provider": EMBEDDED_BACKEND_ID,
33
+ "optional_providers": ["ollama"],
34
+ "embedded": embedded_mini_brain_status(),
32
35
  "mini_brain": contract,
33
36
  "workers": [{"id": worker_id, "purpose": purpose} for worker_id, purpose in LOCAL_WORKERS],
34
37
  "models": {
@@ -42,12 +45,14 @@ def local_llm_doctor() -> dict[str, Any]:
42
45
  status = ollama_status()
43
46
  contract = mini_brain_contract(ollama_payload=status)
44
47
  model_plan = build_model_plan("resuma estes logs operacionais")
45
- ok = status.get("status") == "ok" and contract.get("enabled") is True
48
+ ok = contract.get("available") is True
46
49
  return {
47
50
  "kind": "local-llm-doctor",
48
51
  "schema_version": LOCAL_LLM_SCHEMA_VERSION,
49
52
  "status": "ok" if ok else "partial",
50
- "provider": "ollama",
53
+ "provider": EMBEDDED_BACKEND_ID,
54
+ "optional_providers": ["ollama"],
55
+ "embedded": embedded_mini_brain_status(),
51
56
  "ollama": status,
52
57
  "mini_brain": contract,
53
58
  "model_plan": {
@@ -63,9 +68,19 @@ def local_llm_doctor() -> dict[str, Any]:
63
68
 
64
69
  def local_llm_models() -> dict[str, Any]:
65
70
  payload = ollama_models()
71
+ embedded = embedded_mini_brain_status()
66
72
  payload["kind"] = "local-llm-models"
67
73
  payload["schema_version"] = LOCAL_LLM_SCHEMA_VERSION
68
- payload["provider"] = "ollama"
74
+ payload["provider"] = EMBEDDED_BACKEND_ID
75
+ payload["embedded"] = {
76
+ "status": embedded.get("status"),
77
+ "provider": EMBEDDED_BACKEND_ID,
78
+ "model": EMBEDDED_MODEL_ID,
79
+ "installed": embedded.get("model_file_valid") is True,
80
+ "available": embedded.get("available") is True,
81
+ "install_command": embedded.get("install_command"),
82
+ }
83
+ payload["optional_provider"] = "ollama"
69
84
  return payload
70
85
 
71
86
 
@@ -24,7 +24,7 @@ FORBIDDEN_DELEGATION_MARKERS = (
24
24
 
25
25
 
26
26
  def maybe_delegate_local_llm(prompt: str, model_plan: dict[str, Any]) -> dict[str, Any]:
27
- """Execute a bounded operational task with Ollama when the model plan selected it."""
27
+ """Execute a bounded operational task with the selected local worker."""
28
28
  delegation = model_plan.get("delegation") if isinstance(model_plan.get("delegation"), dict) else {}
29
29
  if model_plan.get("strategy") != "mini-brain":
30
30
  return skipped(
@@ -38,6 +38,12 @@ def maybe_delegate_local_llm(prompt: str, model_plan: dict[str, Any]) -> dict[st
38
38
  "High-risk tasks cannot be delegated to local LLM workers.",
39
39
  model_plan=model_plan,
40
40
  )
41
+ if model_plan.get("local_llm_role") != "operational-worker":
42
+ return skipped(
43
+ "not-operational-worker",
44
+ "The embedded mini-brain is acting as the bootstrap coordinator, not as a delegated worker.",
45
+ model_plan=model_plan,
46
+ )
41
47
  if int(model_plan.get("max_llm_calls") or 0) <= 0:
42
48
  return skipped(
43
49
  "llm-budget-not-available",
@@ -50,9 +56,10 @@ def maybe_delegate_local_llm(prompt: str, model_plan: dict[str, Any]) -> dict[st
50
56
  if any(marker in lowered for marker in FORBIDDEN_DELEGATION_MARKERS):
51
57
  return skipped("forbidden", "Prompt contains an action that local LLM workers cannot execute.", model_plan=model_plan)
52
58
  delegated_prompt = build_delegated_prompt(prompt, model_plan)
59
+ provider = str(model_plan.get("local_llm_provider") or "ollama")
53
60
  result = invoke_agent_prompt(
54
61
  delegated_prompt,
55
- "ollama",
62
+ provider,
56
63
  public_name="Local LLM Operator",
57
64
  allow_fallback=False,
58
65
  )
@@ -64,7 +71,7 @@ def maybe_delegate_local_llm(prompt: str, model_plan: dict[str, Any]) -> dict[st
64
71
  "status": "ok" if result.get("ok") else result.get("status", "failed"),
65
72
  "ok": bool(result.get("ok")),
66
73
  "llm_backend": result.get("llm_backend"),
67
- "model_provider": "ollama",
74
+ "model_provider": provider,
68
75
  "mini_brain": summarize_mini_brain(model_plan.get("mini_brain")),
69
76
  "strategy": model_plan.get("strategy"),
70
77
  "risk": model_plan.get("risk"),
@@ -108,7 +115,7 @@ def enrich_prompt_with_local_result(prompt: str, local_execution: dict[str, Any]
108
115
  [
109
116
  prompt,
110
117
  "",
111
- "Contexto operacional produzido pelo local-llm-operator/Ollama:",
118
+ f"Contexto operacional produzido pelo local-llm-operator/{local_execution.get('model_provider') or local_execution.get('llm_backend') or 'local'}:",
112
119
  str(local_execution["response"]),
113
120
  "",
114
121
  "Use esse contexto apenas como apoio. A decisao, resposta final e revisao continuam sob responsabilidade do coordenador.",
@@ -131,7 +138,10 @@ def skipped(reason: str, message: str, *, model_plan: dict[str, Any]) -> dict[st
131
138
  "strategy": model_plan.get("strategy"),
132
139
  "risk": model_plan.get("risk"),
133
140
  "confidence": model_plan.get("confidence"),
134
- "requires_review": bool(model_plan.get("local_llm_recommended") or model_plan.get("local_llm_selected")),
141
+ "requires_review": bool(
142
+ model_plan.get("local_llm_role") == "operational-worker"
143
+ and (model_plan.get("local_llm_recommended") or model_plan.get("local_llm_selected"))
144
+ ),
135
145
  }
136
146
 
137
147
 
@@ -5,14 +5,20 @@ from __future__ import annotations
5
5
  from datetime import datetime, timezone
6
6
  from typing import Any
7
7
 
8
+ from cli.aikit.embedded_mini_brain import (
9
+ EMBEDDED_BACKEND_ID,
10
+ EMBEDDED_MODEL_ID,
11
+ embedded_mini_brain_status,
12
+ setup_embedded_mini_brain,
13
+ )
8
14
  from cli.aikit.llm import BACKENDS, configure_backend, doctor_backend, load_config, save_config
9
- from cli.aikit.ollama import ollama_pull, ollama_status
15
+ from cli.aikit.ollama import ollama_status
10
16
 
11
17
 
12
18
  MINI_BRAIN_CONFIG_KEY = "mini_brain"
13
- DEFAULT_HF_MODEL = "Qwen/Qwen3-0.6B"
19
+ DEFAULT_HF_MODEL = EMBEDDED_MODEL_ID
14
20
  DEFAULT_OLLAMA_MODEL = "qwen3:0.6b"
15
- DEFAULT_PROVIDER = "ollama"
21
+ DEFAULT_PROVIDER = EMBEDDED_BACKEND_ID
16
22
  DEFAULT_BASE_URL = "http://localhost:11434/v1"
17
23
  ALLOWED_TASKS = [
18
24
  "setup_help",
@@ -50,14 +56,15 @@ def mini_brain_contract(
50
56
  ) -> dict[str, Any]:
51
57
  config = load_config() if config is None else config
52
58
  stored = config.get(MINI_BRAIN_CONFIG_KEY) if isinstance(config.get(MINI_BRAIN_CONFIG_KEY), dict) else {}
53
- enabled = bool(stored.get("enabled"))
59
+ enabled = bool(stored.get("enabled", True))
54
60
  provider = stored.get("provider") or stored.get("runtime") or DEFAULT_PROVIDER
55
61
  hf_model = stored.get("hf_model") or stored.get("model") or DEFAULT_HF_MODEL
56
62
  ollama_model = stored.get("ollama_model") or DEFAULT_OLLAMA_MODEL
63
+ embedded = embedded_mini_brain_status()
57
64
  ollama_payload = ollama_status() if ollama_payload is None else ollama_payload
58
65
  ollama_backend = doctor_backend(BACKENDS["ollama"], config) if ollama_backend is None else ollama_backend
59
- backend_configured = ollama_backend.get("status") == "ok"
60
- runtime_available = ollama_payload.get("status") == "ok" or backend_configured
66
+ ollama_configured = ollama_backend.get("configured") is True
67
+ runtime_available = embedded.get("available") is True
61
68
  available = enabled and provider == DEFAULT_PROVIDER and runtime_available
62
69
  status = "ok" if available else "disabled" if not enabled else "unavailable"
63
70
  return {
@@ -65,7 +72,8 @@ def mini_brain_contract(
65
72
  "status": status,
66
73
  "enabled": enabled,
67
74
  "available": available,
68
- "configured": enabled and provider == DEFAULT_PROVIDER and backend_configured,
75
+ "configured": available,
76
+ "embedded_configured": provider == DEFAULT_PROVIDER,
69
77
  "provider": provider,
70
78
  "runtime": provider,
71
79
  "hf_model": hf_model,
@@ -76,6 +84,7 @@ def mini_brain_contract(
76
84
  "limits": dict_value(stored.get("limits"), DEFAULT_LIMITS),
77
85
  "guardrails": list_value(stored.get("guardrails"), DEFAULT_GUARDRAILS),
78
86
  "stored_secret": False,
87
+ "embedded": embedded,
79
88
  "ollama": {
80
89
  "status": ollama_payload.get("status"),
81
90
  "daemon": (ollama_payload.get("daemon") or {}).get("status")
@@ -87,6 +96,7 @@ def mini_brain_contract(
87
96
  "status": ollama_backend.get("status"),
88
97
  "model": ollama_backend.get("model"),
89
98
  "base_url": ollama_backend.get("base_url"),
99
+ "configured": ollama_configured,
90
100
  },
91
101
  }
92
102
 
@@ -98,6 +108,7 @@ def setup_mini_brain(
98
108
  set_default: bool = False,
99
109
  model: str = DEFAULT_OLLAMA_MODEL,
100
110
  ) -> dict[str, Any]:
111
+ embedded = embedded_mini_brain_status()
101
112
  if dry_run or not yes:
102
113
  status = "planned" if dry_run else "needs-confirmation"
103
114
  needs_confirmation = not dry_run and not yes
@@ -110,53 +121,46 @@ def setup_mini_brain(
110
121
  "yes": yes,
111
122
  "stored_secret": False,
112
123
  "mini_brain": planned_contract(model=model),
113
- "pull": ollama_pull(model, yes=False, dry_run=dry_run),
124
+ "embedded": embedded,
125
+ "embedded_install": setup_embedded_mini_brain(dry_run=True, yes=False),
126
+ "ollama_setup": {
127
+ "status": "skipped",
128
+ "ok": True,
129
+ "provider": "ollama",
130
+ "model": model,
131
+ "message": "Ollama is optional; use `agent local-llm install` to add local worker models.",
132
+ },
114
133
  "next_steps": ["agent setup mini-brain --yes"],
115
- "message": "Use --yes to pull Qwen3-0.6B with Ollama and enable the mini-brain.",
134
+ "message": "Use --yes to download and enable the embedded Qwen2.5-0.5B mini-brain.",
116
135
  }
117
136
 
118
- pull = ollama_pull(model, yes=True, dry_run=False)
119
- toolchain_install = None
120
- if pull.get("status") == "missing":
121
- from cli.aikit.toolchain import install_toolchain
122
-
123
- toolchain_install = install_toolchain(None, "ollama", dry_run=False, yes=True)
124
- if toolchain_install.get("status") == "installed":
125
- pull = ollama_pull(model, yes=True, dry_run=False)
126
- if not pull.get("ok"):
127
- payload = {
137
+ embedded_install = setup_embedded_mini_brain(dry_run=False, yes=True)
138
+ embedded = embedded_mini_brain_status()
139
+ if embedded_install.get("ok") is not True:
140
+ return {
128
141
  "kind": "mini-brain-setup",
129
142
  "status": "failed",
130
143
  "ok": False,
131
- "exit_code": int(pull.get("exit_code") or 2),
144
+ "exit_code": embedded_install.get("exit_code", 1),
132
145
  "dry_run": False,
133
146
  "yes": True,
134
147
  "stored_secret": False,
135
- "mini_brain": planned_contract(model=model),
136
- "pull": pull,
137
- "next_steps": ["Install Ollama or run agent ollama pull qwen3:0.6b --yes"],
138
- "message": pull.get("message") or "Could not pull the mini-brain model.",
148
+ "mini_brain": mini_brain_contract(),
149
+ "embedded": embedded,
150
+ "embedded_install": embedded_install,
151
+ "ollama_setup": {
152
+ "status": "skipped",
153
+ "ok": True,
154
+ "provider": "ollama",
155
+ "model": model,
156
+ "message": "Ollama remains optional for additional local worker models.",
157
+ },
158
+ "message": "Embedded mini-brain setup failed before the backend could be enabled.",
139
159
  }
140
- if toolchain_install:
141
- payload["toolchain_install"] = toolchain_install
142
- payload["next_steps"] = [
143
- "Review `agent toolchain doctor ollama`.",
144
- "Run `agent toolchain install ollama --yes` if you approve external installation.",
145
- "Then run `agent setup mini-brain --yes` again.",
146
- ]
147
- return payload
148
-
149
- existing_config = load_config()
150
- existing_ollama = (
151
- existing_config.get("llm", {}).get("backends", {}).get(DEFAULT_PROVIDER)
152
- if isinstance(existing_config.get("llm"), dict)
153
- else {}
154
- )
155
- existing_base_url = existing_ollama.get("base_url") if isinstance(existing_ollama, dict) else None
160
+
156
161
  configured = configure_backend(
157
162
  DEFAULT_PROVIDER,
158
- base_url=existing_base_url or DEFAULT_BASE_URL,
159
- model=model,
163
+ model=DEFAULT_HF_MODEL,
160
164
  set_default=set_default,
161
165
  )
162
166
  config = load_config()
@@ -172,8 +176,15 @@ def setup_mini_brain(
172
176
  "stored_secret": False,
173
177
  "config_path": str(written_path),
174
178
  "mini_brain": contract,
175
- "pull": pull,
176
- "toolchain_install": toolchain_install,
179
+ "embedded": embedded,
180
+ "embedded_install": embedded_install,
181
+ "ollama_setup": {
182
+ "status": "skipped",
183
+ "ok": True,
184
+ "provider": "ollama",
185
+ "model": model,
186
+ "message": "Ollama remains optional for additional local worker models.",
187
+ },
177
188
  "llm_configure": configured,
178
189
  "next_steps": ["Use low-risk setup, wizard and summary prompts normally."],
179
190
  }
@@ -196,6 +207,7 @@ def planned_contract(*, model: str = DEFAULT_OLLAMA_MODEL) -> dict[str, Any]:
196
207
  "limits": dict(DEFAULT_LIMITS),
197
208
  "guardrails": list(DEFAULT_GUARDRAILS),
198
209
  "stored_secret": False,
210
+ "embedded": embedded_mini_brain_status(),
199
211
  }
200
212
 
201
213
 
@@ -5,6 +5,7 @@ from __future__ import annotations
5
5
  import re
6
6
  from typing import Any
7
7
 
8
+ from cli.aikit.embedded_mini_brain import EMBEDDED_BACKEND_ID
8
9
  from cli.aikit.llm import BACKENDS, doctor_backend, llm_preference, load_config
9
10
  from cli.aikit.mini_brain import mini_brain_contract
10
11
  from cli.aikit.ollama import ollama_status
@@ -14,6 +15,9 @@ from cli.aikit.write_policy import normalize_write_policy, write_policy_public_f
14
15
  OPERATIONAL_PATTERN = re.compile(
15
16
  r"(?i)\b(resum\w*|sumari\w*|classifi\w*|extra(?:i|ir|ia|cao|ção)\w*|normaliz\w*|compar\w*|logs?|rascunho|agrupe|agrupar)\b"
16
17
  )
18
+ SIMPLE_CHAT_SETUP_PATTERN = re.compile(
19
+ r"(?i)\b(ol[aá]|oi|bom dia|boa tarde|boa noite|ajuda|help|comec(?:ar|o)|começ(?:ar|o)|setup|onboard|configur|instal|usar)\b"
20
+ )
17
21
  HIGH_LEVEL_PATTERN = re.compile(
18
22
  r"(?i)\b(arquitet|decid|aprovar|reprovar|especifica|requisit|implemente|codigo|c[oó]digo|documento|automac|deploy|seguran)\b"
19
23
  )
@@ -48,7 +52,9 @@ def build_model_plan(
48
52
  mini_brain = mini_brain_contract(config=config, ollama_payload=ollama, ollama_backend=ollama_backend)
49
53
  local_available = mini_brain.get("available") is True
50
54
  operational = bool(OPERATIONAL_PATTERN.search(prompt))
55
+ simple_chat_setup = bool(SIMPLE_CHAT_SETUP_PATTERN.search(prompt))
51
56
  high_level = bool(HIGH_LEVEL_PATTERN.search(prompt))
57
+ local_provider = select_local_provider(ollama_payload=ollama, ollama_backend=ollama_backend, mini_brain=mini_brain)
52
58
  policy = choose_model_strategy(
53
59
  prompt,
54
60
  route=route,
@@ -56,10 +62,12 @@ def build_model_plan(
56
62
  specialist_tasks=specialist_tasks or [],
57
63
  configuration_tasks=configuration_tasks or [],
58
64
  operational=operational,
65
+ simple_chat_setup=simple_chat_setup,
59
66
  high_level=high_level,
60
67
  local_available=local_available,
61
68
  )
62
69
  use_local = policy["strategy"] == "mini-brain" and local_available
70
+ delegate_local = use_local and operational
63
71
  return {
64
72
  "kind": "model-plan",
65
73
  "status": "planned",
@@ -73,22 +81,30 @@ def build_model_plan(
73
81
  "max_llm_calls": policy["max_llm_calls"],
74
82
  "intent": route.get("intent") if route else "llm",
75
83
  "primary_coordinators": coordinator_order(preference),
76
- "local_llm_role": "operational-worker",
84
+ "local_llm_role": "operational-worker" if operational else "bootstrap-coordinator",
77
85
  "local_llm_available": local_available,
78
- "local_llm_provider": mini_brain.get("provider") or "ollama",
79
- "local_llm_backend_configured": ollama_backend.get("status") == "ok",
86
+ "local_llm_provider": local_provider,
87
+ "local_llm_backend_configured": ollama_backend.get("configured") is True if local_provider == "ollama" else True,
80
88
  "local_llm_runtime": {
81
- "binary_status": ollama.get("status"),
89
+ "provider": local_provider,
90
+ "binary_status": ollama.get("status") if local_provider == "ollama" else "embedded",
82
91
  "backend_status": ollama_backend.get("status"),
83
- "model": mini_brain.get("ollama_model") or ollama_backend.get("model"),
84
- "base_url": ollama_backend.get("base_url"),
92
+ "model": (mini_brain.get("ollama_model") or ollama_backend.get("model")) if local_provider == "ollama" else mini_brain.get("hf_model"),
93
+ "base_url": ollama_backend.get("base_url") if local_provider == "ollama" else None,
94
+ },
95
+ "optional_local_providers": {
96
+ "ollama": {
97
+ "status": ollama.get("status"),
98
+ "backend_status": ollama_backend.get("status"),
99
+ "model_count": ollama.get("model_count"),
100
+ }
85
101
  },
86
102
  "mini_brain": mini_brain,
87
103
  "local_llm_recommended": operational,
88
104
  "local_llm_selected": use_local,
89
105
  "delegation": {
90
- "allowed": policy["strategy"] == "mini-brain",
91
- "selected": use_local,
106
+ "allowed": policy["strategy"] == "mini-brain" and operational,
107
+ "selected": delegate_local,
92
108
  "reason": local_reason(
93
109
  operational=operational,
94
110
  local_available=local_available,
@@ -110,6 +126,7 @@ def choose_model_strategy(
110
126
  specialist_tasks: list[dict[str, Any]],
111
127
  configuration_tasks: list[dict[str, Any]],
112
128
  operational: bool,
129
+ simple_chat_setup: bool,
113
130
  high_level: bool,
114
131
  local_available: bool,
115
132
  ) -> dict[str, Any]:
@@ -154,7 +171,7 @@ def choose_model_strategy(
154
171
  max_llm_calls=0,
155
172
  matrix="Conhecida + estruturada + baixo risco -> automacao",
156
173
  )
157
- if operational and not high_level:
174
+ if (operational or simple_chat_setup) and not high_level:
158
175
  return policy(
159
176
  "mini-brain" if local_available else "external-llm",
160
177
  "The prompt is operational and low-risk; local mini-brain is preferred when available.",
@@ -241,3 +258,19 @@ def local_reason(*, operational: bool, local_available: bool, high_level: bool,
241
258
  if operational and not local_available:
242
259
  return "Task is operational, but the local mini-brain is not enabled or available; coordinator/API fallback should execute."
243
260
  return "Task requires coordinator-level reasoning or review."
261
+
262
+
263
+ def select_local_provider(
264
+ *,
265
+ ollama_payload: dict[str, Any],
266
+ ollama_backend: dict[str, Any],
267
+ mini_brain: dict[str, Any],
268
+ ) -> str:
269
+ ollama_ready = (
270
+ ollama_payload.get("status") == "ok"
271
+ and ollama_backend.get("status") == "ok"
272
+ and (ollama_backend.get("configured") is True or int(ollama_payload.get("model_count") or 0) > 0)
273
+ )
274
+ if ollama_ready:
275
+ return "ollama"
276
+ return str(mini_brain.get("provider") or EMBEDDED_BACKEND_ID)
@@ -62,6 +62,21 @@ def clean_requested_name(value: str) -> str | None:
62
62
  return cleaned[:80]
63
63
 
64
64
 
65
+ def rename_response(updated: dict[str, Any]) -> str:
66
+ name = updated.get("agent_name")
67
+ alias = updated.get("alias") if isinstance(updated.get("alias"), dict) else {}
68
+ alias_name = alias.get("name") or alias.get("suggested_name")
69
+ path_status = alias.get("path_status") if isinstance(alias.get("path_status"), dict) else {}
70
+ if alias_name and path_status.get("setup_required"):
71
+ return (
72
+ f"Pronto. Meu nome local agora e {name}, e criei o comando {alias_name}. "
73
+ "Para chama-lo diretamente em novas sessoes do shell, execute `agent alias path --yes`."
74
+ )
75
+ if alias_name:
76
+ return f"Pronto. Meu nome local agora e {name}, e voce tambem pode me chamar com o comando {alias_name}."
77
+ return f"Pronto. Meu nome local agora e {name}."
78
+
79
+
65
80
  def is_capabilities_help_prompt(prompt: str) -> bool:
66
81
  normalized = normalize_text(prompt)
67
82
  if normalized in {"ajuda", "help", "o que voce faz", "o que voce consegue fazer"}:
@@ -138,6 +153,42 @@ def local_capabilities_help_response(prompt: str, *, name: str) -> dict[str, Any
138
153
  }
139
154
 
140
155
 
156
+ def embedded_mini_brain_install_response(prompt: str, *, name: str, model_plan: dict[str, Any]) -> dict[str, Any]:
157
+ embedded = (
158
+ ((model_plan.get("mini_brain") or {}).get("embedded") or {})
159
+ if isinstance(model_plan.get("mini_brain"), dict)
160
+ else {}
161
+ )
162
+ status = embedded.get("status") or "not-installed"
163
+ response = (
164
+ f"Eu sou {name}. Consigo orientar o setup inicial localmente, mas o mini-cerebro local ainda nao esta instalado "
165
+ f"(status: {status}). Para habilitar conversa local sem Claude, Codex, Ollama ou API externa, execute "
166
+ "`agent setup mini-brain --yes`. Sem esse download, posso continuar com onboarding, memoria, wizards e "
167
+ "capabilities deterministicas."
168
+ )
169
+ return {
170
+ "kind": "agent",
171
+ "status": "needs-setup",
172
+ "ok": False,
173
+ "requires_llm": False,
174
+ "prompt_received": True,
175
+ "prompt_length": len(prompt),
176
+ "mode": "embedded-mini-brain-not-installed",
177
+ "identity": {"name": name, "source": "local"},
178
+ "llm_backend": "embedded-mini-brain",
179
+ "mini_brain": model_plan.get("mini_brain"),
180
+ "response": response,
181
+ "message": "Embedded mini-brain is not installed yet.",
182
+ "next_steps": [
183
+ "agent setup mini-brain --dry-run",
184
+ "agent setup mini-brain --yes",
185
+ "agent llm configure claude-code --set-default",
186
+ "agent llm configure codex-cli --set-default",
187
+ ],
188
+ "exit_code": 2,
189
+ }
190
+
191
+
141
192
  def agent_requires_llm(args: argparse.Namespace) -> dict[str, Any]:
142
193
  prompt = " ".join(args.prompt).strip()
143
194
  return run_agent_prompt_request(
@@ -184,7 +235,8 @@ def run_agent_prompt_request(request: AgentPromptRequest) -> dict[str, Any]:
184
235
  "prompt_length": len(prompt),
185
236
  "identity": {"name": updated.get("agent_name"), "source": "local"},
186
237
  "action": "rename",
187
- "response": f"Pronto. Meu nome local agora e {updated.get('agent_name')}.",
238
+ "response": rename_response(updated),
239
+ "alias": updated.get("alias"),
188
240
  }
189
241
  return finalize_agent_session(result, session, prompt, backend=request.llm)
190
242
  if is_identity_question(prompt):
@@ -237,9 +289,15 @@ def run_agent_prompt_request(request: AgentPromptRequest) -> dict[str, Any]:
237
289
  )
238
290
  local_llm_execution = maybe_delegate_local_llm(prompt, model_plan)
239
291
  coordinator_prompt = enrich_prompt_with_local_result(contextual_prompt, local_llm_execution)
292
+ requested_backend = request.llm
293
+ if should_prompt_for_embedded_install(model_plan, requested_backend=request.llm):
294
+ result = embedded_mini_brain_install_response(prompt, name=name, model_plan=model_plan)
295
+ return finalize_agent_session(result, session, prompt, backend="embedded-mini-brain")
296
+ if should_use_embedded_coordinator(model_plan, requested_backend=request.llm):
297
+ requested_backend = "embedded-mini-brain"
240
298
  result = invoke_agent_prompt(
241
299
  coordinator_prompt,
242
- request.llm,
300
+ requested_backend,
243
301
  public_name=name,
244
302
  allow_fallback=not request.no_llm_fallback,
245
303
  )
@@ -278,6 +336,32 @@ def run_agent_prompt_request(request: AgentPromptRequest) -> dict[str, Any]:
278
336
  return finalize_agent_session(result, session, prompt, backend=result.get("llm_backend") or request.llm)
279
337
 
280
338
 
339
+ def should_use_embedded_coordinator(model_plan: dict[str, Any], *, requested_backend: str | None) -> bool:
340
+ if requested_backend:
341
+ return False
342
+ return (
343
+ model_plan.get("strategy") == "mini-brain"
344
+ and model_plan.get("local_llm_provider") == "embedded-mini-brain"
345
+ and model_plan.get("risk") == "low"
346
+ )
347
+
348
+
349
+ def should_prompt_for_embedded_install(model_plan: dict[str, Any], *, requested_backend: str | None) -> bool:
350
+ if requested_backend:
351
+ return False
352
+ embedded = (
353
+ ((model_plan.get("mini_brain") or {}).get("embedded") or {})
354
+ if isinstance(model_plan.get("mini_brain"), dict)
355
+ else {}
356
+ )
357
+ return (
358
+ model_plan.get("strategy") in {"mini-brain", "external-llm"}
359
+ and model_plan.get("local_llm_provider") == "embedded-mini-brain"
360
+ and embedded.get("available") is not True
361
+ and model_plan.get("fallback") == "configure-local-mini-brain-or-use-external-llm"
362
+ )
363
+
364
+
281
365
  def mark_review_task_needs_review(execution_plan: dict[str, Any], review_result: dict[str, Any]) -> dict[str, Any]:
282
366
  task = dict(execution_plan.get("review_task") or {})
283
367
  if task:
@@ -106,16 +106,16 @@ def onboarding_plan(root: Path, mode: str) -> dict[str, Any]:
106
106
  ),
107
107
  plan_step(
108
108
  "coordinator-llm",
109
- "Registrar Claude Code, Codex CLI ou API como coordenador/planejador/revisor.",
109
+ "Registrar Claude Code, Codex CLI ou API como coordenador/planejador/revisor opcional para tarefas de alto nivel.",
110
110
  "agent llm list",
111
111
  write_policy="local_config_write",
112
112
  ),
113
113
  plan_step(
114
114
  "mini-brain",
115
- "Habilitar Qwen3-0.6B via Ollama para conversa simples, setup e tarefas operacionais leves.",
115
+ "Validar o mini cerebro embarcado Qwen2.5-0.5B para conversa simples, setup e tarefas operacionais leves.",
116
116
  "agent setup mini-brain --dry-run",
117
117
  write_policy="local_config_write",
118
- model="qwen3:0.6b",
118
+ model="Qwen/Qwen2.5-0.5B-Instruct",
119
119
  ),
120
120
  plan_step(
121
121
  "sessions-and-memory",
@@ -6,6 +6,7 @@ import re
6
6
  from pathlib import Path
7
7
  from typing import Any
8
8
 
9
+ from cli.aikit.aliases import ensure_alias_for_agent_name
9
10
  from cli.aikit.identity import DEFAULT_PUBLIC_NAME
10
11
  from cli.aikit.memory import MEMORY_FILE_TEMPLATES, ensure_memory, memory_home
11
12
 
@@ -60,6 +61,8 @@ def update_personality(
60
61
  path.write_text(render_personality(values), encoding="utf-8")
61
62
  payload = load_personality()
62
63
  payload["status"] = "updated"
64
+ if agent_name is not None and payload.get("agent_name") != DEFAULT_PUBLIC_NAME:
65
+ payload["alias"] = ensure_alias_for_agent_name(str(payload["agent_name"]))
63
66
  return payload
64
67
 
65
68