agent-devkit 0.2.0 → 0.3.0
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.
- package/README.md +48 -6
- package/bin/agent.mjs +133 -7
- package/package.json +1 -1
- package/runtime/README.md +187 -5
- package/runtime/agent +31 -5
- package/runtime/agents/README.md +18 -0
- package/runtime/agents/contribution-reviewer/AGENTS.md +8 -0
- package/runtime/agents/contribution-reviewer/README.md +8 -0
- package/runtime/agents/contribution-reviewer/agent.yaml +40 -0
- package/runtime/agents/contribution-reviewer/capabilities/plan-contribution-pr/capability.yaml +27 -0
- package/runtime/agents/contribution-reviewer/capabilities/plan-contribution-pr/decision-rules.md +5 -0
- package/runtime/agents/contribution-reviewer/capabilities/plan-contribution-pr/workflow.md +6 -0
- package/runtime/agents/contribution-reviewer/capabilities/review-contribution/capability.yaml +25 -0
- package/runtime/agents/contribution-reviewer/capabilities/review-contribution/decision-rules.md +5 -0
- package/runtime/agents/contribution-reviewer/capabilities/review-contribution/workflow.md +5 -0
- package/runtime/agents/contribution-reviewer/capabilities/validate-local-contribution/capability.yaml +26 -0
- package/runtime/agents/contribution-reviewer/capabilities/validate-local-contribution/decision-rules.md +5 -0
- package/runtime/agents/contribution-reviewer/capabilities/validate-local-contribution/workflow.md +6 -0
- package/runtime/agents/contribution-reviewer/infra/README.md +6 -0
- package/runtime/agents/contribution-reviewer/knowledge/context.md +8 -0
- package/runtime/agents/contribution-reviewer/knowledge/system.md +8 -0
- package/runtime/agents/contribution-reviewer/templates/README.md +3 -0
- package/runtime/agents/knowledge-author/AGENTS.md +7 -0
- package/runtime/agents/knowledge-author/README.md +7 -0
- package/runtime/agents/knowledge-author/agent.yaml +37 -0
- package/runtime/agents/knowledge-author/capabilities/create-knowledge-snapshot/capability.yaml +30 -0
- package/runtime/agents/knowledge-author/capabilities/create-knowledge-snapshot/decision-rules.md +6 -0
- package/runtime/agents/knowledge-author/capabilities/create-knowledge-snapshot/workflow.md +7 -0
- package/runtime/agents/knowledge-author/infra/.gitkeep +1 -0
- package/runtime/agents/knowledge-author/knowledge/context.md +4 -0
- package/runtime/agents/knowledge-author/knowledge/system.md +4 -0
- package/runtime/agents/knowledge-author/templates/.gitkeep +1 -0
- package/runtime/agents/knowledge-curator/AGENTS.md +7 -0
- package/runtime/agents/knowledge-curator/README.md +6 -0
- package/runtime/agents/knowledge-curator/agent.yaml +37 -0
- package/runtime/agents/knowledge-curator/capabilities/curate-knowledge-base/capability.yaml +29 -0
- package/runtime/agents/knowledge-curator/capabilities/curate-knowledge-base/decision-rules.md +6 -0
- package/runtime/agents/knowledge-curator/capabilities/curate-knowledge-base/workflow.md +7 -0
- package/runtime/agents/knowledge-curator/infra/.gitkeep +1 -0
- package/runtime/agents/knowledge-curator/knowledge/context.md +4 -0
- package/runtime/agents/knowledge-curator/knowledge/system.md +4 -0
- package/runtime/agents/knowledge-curator/templates/.gitkeep +1 -0
- package/runtime/agents/knowledge-infra-builder/AGENTS.md +8 -0
- package/runtime/agents/knowledge-infra-builder/README.md +8 -0
- package/runtime/agents/knowledge-infra-builder/agent.yaml +38 -0
- package/runtime/agents/knowledge-infra-builder/capabilities/create-knowledge-base/capability.yaml +30 -0
- package/runtime/agents/knowledge-infra-builder/capabilities/create-knowledge-base/decision-rules.md +6 -0
- package/runtime/agents/knowledge-infra-builder/capabilities/create-knowledge-base/workflow.md +7 -0
- package/runtime/agents/knowledge-infra-builder/infra/.gitkeep +1 -0
- package/runtime/agents/knowledge-infra-builder/knowledge/context.md +4 -0
- package/runtime/agents/knowledge-infra-builder/knowledge/system.md +4 -0
- package/runtime/agents/knowledge-infra-builder/templates/.gitkeep +1 -0
- package/runtime/agents/knowledge-owner/AGENTS.md +7 -0
- package/runtime/agents/knowledge-owner/README.md +6 -0
- package/runtime/agents/knowledge-owner/agent.yaml +37 -0
- package/runtime/agents/knowledge-owner/capabilities/publish-knowledge-snapshot/capability.yaml +28 -0
- package/runtime/agents/knowledge-owner/capabilities/publish-knowledge-snapshot/decision-rules.md +6 -0
- package/runtime/agents/knowledge-owner/capabilities/publish-knowledge-snapshot/workflow.md +7 -0
- package/runtime/agents/knowledge-owner/infra/.gitkeep +1 -0
- package/runtime/agents/knowledge-owner/knowledge/context.md +4 -0
- package/runtime/agents/knowledge-owner/knowledge/system.md +4 -0
- package/runtime/agents/knowledge-owner/templates/.gitkeep +1 -0
- package/runtime/agents/knowledge-reviewer/AGENTS.md +7 -0
- package/runtime/agents/knowledge-reviewer/README.md +7 -0
- package/runtime/agents/knowledge-reviewer/agent.yaml +36 -0
- package/runtime/agents/knowledge-reviewer/capabilities/review-knowledge-snapshot/capability.yaml +26 -0
- package/runtime/agents/knowledge-reviewer/capabilities/review-knowledge-snapshot/decision-rules.md +6 -0
- package/runtime/agents/knowledge-reviewer/capabilities/review-knowledge-snapshot/workflow.md +7 -0
- package/runtime/agents/knowledge-reviewer/infra/.gitkeep +1 -0
- package/runtime/agents/knowledge-reviewer/knowledge/context.md +4 -0
- package/runtime/agents/knowledge-reviewer/knowledge/system.md +4 -0
- package/runtime/agents/knowledge-reviewer/templates/.gitkeep +1 -0
- package/runtime/agents/local-memory-manager/AGENTS.md +5 -0
- package/runtime/agents/local-memory-manager/README.md +7 -0
- package/runtime/agents/local-memory-manager/agent.yaml +38 -0
- package/runtime/agents/local-memory-manager/capabilities/curate-local-memory/capability.yaml +19 -0
- package/runtime/agents/local-memory-manager/capabilities/curate-local-memory/decision-rules.md +5 -0
- package/runtime/agents/local-memory-manager/capabilities/curate-local-memory/workflow.md +6 -0
- package/runtime/agents/local-memory-manager/capabilities/inspect-local-memory/capability.yaml +19 -0
- package/runtime/agents/local-memory-manager/capabilities/inspect-local-memory/decision-rules.md +5 -0
- package/runtime/agents/local-memory-manager/capabilities/inspect-local-memory/workflow.md +5 -0
- package/runtime/agents/local-memory-manager/infra/.gitkeep +1 -0
- package/runtime/agents/local-memory-manager/knowledge/context.md +4 -0
- package/runtime/agents/local-memory-manager/knowledge/system.md +4 -0
- package/runtime/agents/local-memory-manager/templates/.gitkeep +1 -0
- package/runtime/agents/memory-sync-manager/AGENTS.md +7 -0
- package/runtime/agents/memory-sync-manager/README.md +7 -0
- package/runtime/agents/memory-sync-manager/agent.yaml +37 -0
- package/runtime/agents/memory-sync-manager/capabilities/plan-memory-backup/capability.yaml +29 -0
- package/runtime/agents/memory-sync-manager/capabilities/plan-memory-backup/decision-rules.md +6 -0
- package/runtime/agents/memory-sync-manager/capabilities/plan-memory-backup/workflow.md +7 -0
- package/runtime/agents/memory-sync-manager/infra/.gitkeep +1 -0
- package/runtime/agents/memory-sync-manager/knowledge/context.md +4 -0
- package/runtime/agents/memory-sync-manager/knowledge/system.md +4 -0
- package/runtime/agents/memory-sync-manager/templates/.gitkeep +1 -0
- package/runtime/agents/shared-memory-curator/AGENTS.md +5 -0
- package/runtime/agents/shared-memory-curator/README.md +6 -0
- package/runtime/agents/shared-memory-curator/agent.yaml +38 -0
- package/runtime/agents/shared-memory-curator/capabilities/create-shared-memory/capability.yaml +19 -0
- package/runtime/agents/shared-memory-curator/capabilities/create-shared-memory/decision-rules.md +5 -0
- package/runtime/agents/shared-memory-curator/capabilities/create-shared-memory/workflow.md +5 -0
- package/runtime/agents/shared-memory-curator/capabilities/publish-shared-submission/capability.yaml +19 -0
- package/runtime/agents/shared-memory-curator/capabilities/publish-shared-submission/decision-rules.md +5 -0
- package/runtime/agents/shared-memory-curator/capabilities/publish-shared-submission/workflow.md +5 -0
- package/runtime/agents/shared-memory-curator/capabilities/review-shared-submission/capability.yaml +19 -0
- package/runtime/agents/shared-memory-curator/capabilities/review-shared-submission/decision-rules.md +5 -0
- package/runtime/agents/shared-memory-curator/capabilities/review-shared-submission/workflow.md +5 -0
- package/runtime/agents/shared-memory-curator/infra/.gitkeep +1 -0
- package/runtime/agents/shared-memory-curator/knowledge/context.md +5 -0
- package/runtime/agents/shared-memory-curator/knowledge/system.md +4 -0
- package/runtime/agents/shared-memory-curator/templates/.gitkeep +1 -0
- package/runtime/cli/README.md +35 -4
- package/runtime/cli/aikit/__init__.py +1 -1
- package/runtime/cli/aikit/agent_registry.py +4 -2
- package/runtime/cli/aikit/agentic_commands.py +158 -0
- package/runtime/cli/aikit/app_home.py +1 -0
- package/runtime/cli/aikit/audit.py +16 -6
- package/runtime/cli/aikit/catalog.py +278 -8
- package/runtime/cli/aikit/cli_dispatch.py +489 -13
- package/runtime/cli/aikit/cli_parser.py +145 -7
- package/runtime/cli/aikit/contribution.py +132 -2
- package/runtime/cli/aikit/doctor_runtime.py +85 -0
- package/runtime/cli/aikit/eval.py +356 -10
- package/runtime/cli/aikit/human_output.py +310 -4
- package/runtime/cli/aikit/interactive_wizard.py +148 -0
- package/runtime/cli/aikit/knowledge_base.py +1067 -0
- package/runtime/cli/aikit/llm.py +12 -4
- package/runtime/cli/aikit/local_artifacts.py +444 -0
- package/runtime/cli/aikit/local_llm.py +161 -0
- package/runtime/cli/aikit/main.py +15 -0
- package/runtime/cli/aikit/mcp_manifest.py +798 -0
- package/runtime/cli/aikit/mcp_tools.py +643 -5
- package/runtime/cli/aikit/memory.py +405 -0
- package/runtime/cli/aikit/mini_brain.py +20 -1
- package/runtime/cli/aikit/natural_prompt_runtime.py +125 -1
- package/runtime/cli/aikit/ollama.py +64 -15
- package/runtime/cli/aikit/onboarding.py +551 -0
- package/runtime/cli/aikit/output.py +67 -0
- package/runtime/cli/aikit/prompt_injection.py +12 -1
- package/runtime/cli/aikit/roadmap_cli.py +1 -1
- package/runtime/cli/aikit/secrets.py +3 -2
- package/runtime/cli/aikit/setup_wizard_payload.py +3 -0
- package/runtime/cli/aikit/shared_memory.py +415 -0
- package/runtime/cli/aikit/specialist_readiness.py +152 -0
- package/runtime/cli/aikit/tasks.py +104 -1
- package/runtime/cli/aikit/team.py +380 -0
- package/runtime/cli/aikit/toolchain.py +7 -2
- package/runtime/cli/aikit/workflows.py +115 -14
- package/runtime/providers/knowledge-github.yaml +40 -0
- package/runtime/providers/knowledge-google-drive.yaml +32 -0
- package/runtime/providers/knowledge-local.yaml +26 -0
- package/runtime/providers/knowledge-notion.yaml +32 -0
- package/runtime/providers/knowledge-obsidian.yaml +24 -0
- package/runtime/providers/knowledge-onedrive.yaml +36 -0
- package/runtime/providers/knowledge-s3.yaml +45 -0
- package/runtime/providers/knowledge-sharepoint.yaml +39 -0
- package/runtime/providers/knowledge-supabase.yaml +43 -0
- package/runtime/providers/knowledge-vector.yaml +39 -0
- package/runtime/requirements.txt +6 -0
- package/runtime/scripts/docker-cli-qa.sh +453 -0
- package/runtime/scripts/release-catalog-snapshot.json +55 -4
- package/runtime/scripts/release-gate.py +54 -13
- package/runtime/tooling/toolchain.yaml +92 -0
- package/runtime/vendor/skills/napkin/napkin.md +21 -7
- package/runtime/workflows/azure-card-analysis/README.md +3 -0
- package/runtime/workflows/azure-card-analysis/workflow.yaml +30 -0
- package/runtime/workflows/daily-pr-review/README.md +3 -0
- package/runtime/workflows/daily-pr-review/workflow.yaml +31 -0
- package/runtime/workflows/incident-analysis/README.md +3 -0
- package/runtime/workflows/incident-analysis/workflow.yaml +33 -0
- package/runtime/workflows/release-prep/README.md +3 -0
- package/runtime/workflows/release-prep/workflow.yaml +30 -0
|
@@ -4,6 +4,14 @@ from __future__ import annotations
|
|
|
4
4
|
|
|
5
5
|
import re
|
|
6
6
|
import shutil
|
|
7
|
+
import hashlib
|
|
8
|
+
import hmac
|
|
9
|
+
import base64
|
|
10
|
+
import io
|
|
11
|
+
import json
|
|
12
|
+
import os
|
|
13
|
+
import tarfile
|
|
14
|
+
import tempfile
|
|
7
15
|
from datetime import datetime, timezone
|
|
8
16
|
from pathlib import Path
|
|
9
17
|
from typing import Any
|
|
@@ -95,12 +103,18 @@ Curated local runbook entries promoted from repeated use.
|
|
|
95
103
|
- Keep high-value reusable notes here.
|
|
96
104
|
""",
|
|
97
105
|
}
|
|
106
|
+
BACKUP_PACKAGE_SCHEMA_VERSION = "agent-devkit.memory-backup-package/v1"
|
|
107
|
+
BACKUP_PACKAGE_ALGORITHM = "PBKDF2-HMAC-SHA256/XOR-HMAC-SHA256"
|
|
98
108
|
|
|
99
109
|
|
|
100
110
|
def memory_home() -> Path:
|
|
101
111
|
return app_memory_home()
|
|
102
112
|
|
|
103
113
|
|
|
114
|
+
def memory_backups_home() -> Path:
|
|
115
|
+
return app_path("backups", "memory")
|
|
116
|
+
|
|
117
|
+
|
|
104
118
|
def ensure_memory() -> dict[str, Any]:
|
|
105
119
|
ensure_app_home()
|
|
106
120
|
home = memory_home()
|
|
@@ -193,6 +207,183 @@ def show_memory(root: Path, *, agent_id: str | None = None, source_id: str | Non
|
|
|
193
207
|
}
|
|
194
208
|
|
|
195
209
|
|
|
210
|
+
def create_memory_backup(
|
|
211
|
+
*,
|
|
212
|
+
title: str | None = None,
|
|
213
|
+
encrypted: bool = False,
|
|
214
|
+
passphrase_env: str | None = None,
|
|
215
|
+
) -> dict[str, Any]:
|
|
216
|
+
memory_paths = ensure_memory()
|
|
217
|
+
backups = memory_backups_home()
|
|
218
|
+
backups.mkdir(parents=True, exist_ok=True)
|
|
219
|
+
backup_id = unique_backup_id(title)
|
|
220
|
+
backup_root = backups / backup_id
|
|
221
|
+
backup_root.mkdir(parents=True, exist_ok=False)
|
|
222
|
+
backup_memory = backup_root / ("_memory-staging" if encrypted else "memory")
|
|
223
|
+
shutil.copytree(memory_home(), backup_memory)
|
|
224
|
+
|
|
225
|
+
files = backup_file_inventory(backup_memory)
|
|
226
|
+
manifest = {
|
|
227
|
+
"schema_version": "agent-devkit.memory-backup/v1",
|
|
228
|
+
"id": backup_id,
|
|
229
|
+
"title": title or "Memory backup",
|
|
230
|
+
"created_at": datetime.now(timezone.utc).isoformat(),
|
|
231
|
+
"source_home": str(memory_home()),
|
|
232
|
+
"storage": "local-filesystem",
|
|
233
|
+
"remote_upload": False,
|
|
234
|
+
"encrypted": encrypted,
|
|
235
|
+
"sensitive_local_copy": not encrypted,
|
|
236
|
+
"files": files,
|
|
237
|
+
"package": None,
|
|
238
|
+
"notes": [
|
|
239
|
+
"This is a local backup only; no remote provider upload was executed.",
|
|
240
|
+
"Remote backup must be encrypted before upload and requires explicit opt-in.",
|
|
241
|
+
],
|
|
242
|
+
}
|
|
243
|
+
if encrypted:
|
|
244
|
+
package_path = backup_root / f"{backup_id}.adkmb"
|
|
245
|
+
passphrase = passphrase_from_env(passphrase_env)
|
|
246
|
+
write_encrypted_backup_package(package_path, backup_memory, manifest, passphrase)
|
|
247
|
+
manifest["package"] = str(package_path)
|
|
248
|
+
manifest["package_name"] = package_path.name
|
|
249
|
+
shutil.rmtree(backup_memory)
|
|
250
|
+
manifest_path = backup_root / "manifest.json"
|
|
251
|
+
manifest_path.write_text(json.dumps(manifest, ensure_ascii=False, indent=2, sort_keys=True) + "\n", encoding="utf-8")
|
|
252
|
+
next_steps = [
|
|
253
|
+
f"Restore with `agent memory backup restore {backup_id} --yes`.",
|
|
254
|
+
f"Delete with `agent memory backup delete {backup_id} --yes`.",
|
|
255
|
+
]
|
|
256
|
+
if encrypted:
|
|
257
|
+
next_steps[0] = f"Restore with `agent memory backup restore {backup_id} --passphrase-env {passphrase_env or 'AGENT_DEVKIT_BACKUP_PASSPHRASE'} --yes`."
|
|
258
|
+
next_steps.insert(1, f"Portable package: {manifest['package']}")
|
|
259
|
+
return {
|
|
260
|
+
"kind": "memory-backup",
|
|
261
|
+
"status": "created",
|
|
262
|
+
"backup": public_backup(manifest, backup_root),
|
|
263
|
+
"home": str(backups),
|
|
264
|
+
"memory_home": memory_paths["home"],
|
|
265
|
+
"next_steps": next_steps,
|
|
266
|
+
}
|
|
267
|
+
|
|
268
|
+
|
|
269
|
+
def list_memory_backups() -> dict[str, Any]:
|
|
270
|
+
backups = memory_backups_home()
|
|
271
|
+
items = []
|
|
272
|
+
if backups.exists():
|
|
273
|
+
for manifest_path in sorted(backups.glob("*/manifest.json")):
|
|
274
|
+
manifest = load_backup_manifest(manifest_path)
|
|
275
|
+
if manifest:
|
|
276
|
+
items.append(public_backup(manifest, manifest_path.parent))
|
|
277
|
+
return {
|
|
278
|
+
"kind": "memory-backups",
|
|
279
|
+
"status": "ok",
|
|
280
|
+
"home": str(backups),
|
|
281
|
+
"count": len(items),
|
|
282
|
+
"items": items,
|
|
283
|
+
}
|
|
284
|
+
|
|
285
|
+
|
|
286
|
+
def restore_memory_backup(
|
|
287
|
+
backup_id: str | None,
|
|
288
|
+
*,
|
|
289
|
+
yes: bool = False,
|
|
290
|
+
backup_file: str | None = None,
|
|
291
|
+
passphrase_env: str | None = None,
|
|
292
|
+
) -> dict[str, Any]:
|
|
293
|
+
backup_root, manifest = require_memory_backup(backup_id, backup_file=backup_file, passphrase_env=passphrase_env)
|
|
294
|
+
payload = {
|
|
295
|
+
"kind": "memory-backup-restore",
|
|
296
|
+
"backup": public_backup(manifest, backup_root),
|
|
297
|
+
"memory_home": str(memory_home()),
|
|
298
|
+
"requires_confirmation": True,
|
|
299
|
+
}
|
|
300
|
+
if not yes:
|
|
301
|
+
return {
|
|
302
|
+
**payload,
|
|
303
|
+
"status": "planned",
|
|
304
|
+
"executed": False,
|
|
305
|
+
"next_steps": [restore_next_step(manifest, backup_file=backup_file, passphrase_env=passphrase_env)],
|
|
306
|
+
}
|
|
307
|
+
|
|
308
|
+
ensure_app_home()
|
|
309
|
+
safety_backup: str | None = None
|
|
310
|
+
with decrypted_memory_source(backup_root, manifest, passphrase_env=passphrase_env) as source_memory:
|
|
311
|
+
if memory_home().exists():
|
|
312
|
+
safety_root = memory_backups_home() / f"pre-restore-{timestamp_id()}"
|
|
313
|
+
safety_root.mkdir(parents=True, exist_ok=False)
|
|
314
|
+
shutil.copytree(memory_home(), safety_root / "memory")
|
|
315
|
+
safety_backup = str(safety_root)
|
|
316
|
+
shutil.rmtree(memory_home())
|
|
317
|
+
shutil.copytree(source_memory, memory_home())
|
|
318
|
+
return {
|
|
319
|
+
**payload,
|
|
320
|
+
"status": "restored",
|
|
321
|
+
"executed": True,
|
|
322
|
+
"safety_backup": safety_backup,
|
|
323
|
+
}
|
|
324
|
+
|
|
325
|
+
|
|
326
|
+
class decrypted_memory_source:
|
|
327
|
+
def __init__(self, backup_root: Path, manifest: dict[str, Any], *, passphrase_env: str | None = None) -> None:
|
|
328
|
+
self.backup_root = backup_root
|
|
329
|
+
self.manifest = manifest
|
|
330
|
+
self.passphrase_env = passphrase_env
|
|
331
|
+
self.temp_dir: tempfile.TemporaryDirectory[str] | None = None
|
|
332
|
+
|
|
333
|
+
def __enter__(self) -> Path:
|
|
334
|
+
plain = self.backup_root / "memory"
|
|
335
|
+
if plain.exists():
|
|
336
|
+
return plain
|
|
337
|
+
package_path = package_path_from_manifest(self.backup_root, self.manifest)
|
|
338
|
+
if not package_path:
|
|
339
|
+
raise ValueError(f"memory backup has no restorable memory payload: {self.manifest.get('id')}")
|
|
340
|
+
passphrase = passphrase_from_env(self.passphrase_env)
|
|
341
|
+
self.temp_dir = tempfile.TemporaryDirectory(prefix="agent-devkit-memory-restore-")
|
|
342
|
+
target = Path(self.temp_dir.name)
|
|
343
|
+
extract_encrypted_backup_package(package_path, target, passphrase)
|
|
344
|
+
memory = target / "memory"
|
|
345
|
+
if not memory.exists():
|
|
346
|
+
raise ValueError("encrypted memory backup package did not contain memory/")
|
|
347
|
+
return memory
|
|
348
|
+
|
|
349
|
+
def __exit__(self, _exc_type: object, _exc: object, _tb: object) -> None:
|
|
350
|
+
if self.temp_dir:
|
|
351
|
+
self.temp_dir.cleanup()
|
|
352
|
+
|
|
353
|
+
|
|
354
|
+
def restore_next_step(manifest: dict[str, Any], *, backup_file: str | None, passphrase_env: str | None) -> str:
|
|
355
|
+
if manifest.get("encrypted"):
|
|
356
|
+
env_name = passphrase_env or "AGENT_DEVKIT_BACKUP_PASSPHRASE"
|
|
357
|
+
if backup_file:
|
|
358
|
+
return f"Re-run with `agent memory backup restore --file {backup_file} --passphrase-env {env_name} --yes` to restore local memory."
|
|
359
|
+
return f"Re-run with `agent memory backup restore {manifest['id']} --passphrase-env {env_name} --yes` to restore local memory."
|
|
360
|
+
if backup_file:
|
|
361
|
+
return f"Re-run with `agent memory backup restore --file {backup_file} --yes` to restore local memory."
|
|
362
|
+
return f"Re-run with `agent memory backup restore {manifest['id']} --yes` to restore local memory."
|
|
363
|
+
|
|
364
|
+
|
|
365
|
+
def delete_memory_backup(backup_id: str | None, *, yes: bool = False) -> dict[str, Any]:
|
|
366
|
+
backup_root, manifest = require_memory_backup(backup_id)
|
|
367
|
+
payload = {
|
|
368
|
+
"kind": "memory-backup-delete",
|
|
369
|
+
"backup": public_backup(manifest, backup_root),
|
|
370
|
+
"requires_confirmation": True,
|
|
371
|
+
}
|
|
372
|
+
if not yes:
|
|
373
|
+
return {
|
|
374
|
+
**payload,
|
|
375
|
+
"status": "planned",
|
|
376
|
+
"executed": False,
|
|
377
|
+
"next_steps": [f"Re-run with `agent memory backup delete {manifest['id']} --yes` to remove this local backup."],
|
|
378
|
+
}
|
|
379
|
+
shutil.rmtree(backup_root)
|
|
380
|
+
return {
|
|
381
|
+
**payload,
|
|
382
|
+
"status": "deleted",
|
|
383
|
+
"executed": True,
|
|
384
|
+
}
|
|
385
|
+
|
|
386
|
+
|
|
196
387
|
def reset_memory(
|
|
197
388
|
*,
|
|
198
389
|
all_memory: bool = False,
|
|
@@ -246,6 +437,220 @@ def memory_path_payload() -> dict[str, Any]:
|
|
|
246
437
|
return ensure_memory()
|
|
247
438
|
|
|
248
439
|
|
|
440
|
+
def unique_backup_id(title: str | None) -> str:
|
|
441
|
+
base = sanitize_segment(title or "memory-backup")
|
|
442
|
+
candidate = f"{base}-{timestamp_id()}"
|
|
443
|
+
backups = memory_backups_home()
|
|
444
|
+
if not (backups / candidate).exists():
|
|
445
|
+
return candidate
|
|
446
|
+
suffix = 2
|
|
447
|
+
while (backups / f"{candidate}-{suffix}").exists():
|
|
448
|
+
suffix += 1
|
|
449
|
+
return f"{candidate}-{suffix}"
|
|
450
|
+
|
|
451
|
+
|
|
452
|
+
def timestamp_id() -> str:
|
|
453
|
+
return datetime.now(timezone.utc).strftime("%Y%m%d%H%M%S")
|
|
454
|
+
|
|
455
|
+
|
|
456
|
+
def backup_file_inventory(root: Path) -> list[dict[str, Any]]:
|
|
457
|
+
items: list[dict[str, Any]] = []
|
|
458
|
+
for path in sorted(item for item in root.rglob("*") if item.is_file()):
|
|
459
|
+
relative = str(path.relative_to(root))
|
|
460
|
+
data = path.read_bytes()
|
|
461
|
+
items.append(
|
|
462
|
+
{
|
|
463
|
+
"path": relative,
|
|
464
|
+
"bytes": len(data),
|
|
465
|
+
"sha256": hashlib.sha256(data).hexdigest(),
|
|
466
|
+
}
|
|
467
|
+
)
|
|
468
|
+
return items
|
|
469
|
+
|
|
470
|
+
|
|
471
|
+
def public_backup(manifest: dict[str, Any], root: Path) -> dict[str, Any]:
|
|
472
|
+
files = manifest.get("files") if isinstance(manifest.get("files"), list) else []
|
|
473
|
+
return {
|
|
474
|
+
"id": manifest.get("id"),
|
|
475
|
+
"title": manifest.get("title"),
|
|
476
|
+
"created_at": manifest.get("created_at"),
|
|
477
|
+
"path": str(root),
|
|
478
|
+
"storage": manifest.get("storage"),
|
|
479
|
+
"remote_upload": manifest.get("remote_upload") is True,
|
|
480
|
+
"encrypted": manifest.get("encrypted") is True,
|
|
481
|
+
"sensitive_local_copy": manifest.get("sensitive_local_copy") is True,
|
|
482
|
+
"package": manifest.get("package"),
|
|
483
|
+
"package_name": manifest.get("package_name"),
|
|
484
|
+
"file_count": len(files),
|
|
485
|
+
}
|
|
486
|
+
|
|
487
|
+
|
|
488
|
+
def require_memory_backup(
|
|
489
|
+
backup_id: str | None,
|
|
490
|
+
*,
|
|
491
|
+
backup_file: str | None = None,
|
|
492
|
+
passphrase_env: str | None = None,
|
|
493
|
+
) -> tuple[Path, dict[str, Any]]:
|
|
494
|
+
if backup_file:
|
|
495
|
+
package = Path(backup_file).expanduser().resolve()
|
|
496
|
+
manifest = read_encrypted_backup_header(package, passphrase_env=passphrase_env)
|
|
497
|
+
return package.parent, manifest
|
|
498
|
+
item_id = sanitize_segment(backup_id or "")
|
|
499
|
+
if not item_id:
|
|
500
|
+
raise ValueError("memory backup requires a backup id")
|
|
501
|
+
root = memory_backups_home() / item_id
|
|
502
|
+
manifest_path = root / "manifest.json"
|
|
503
|
+
if not manifest_path.exists():
|
|
504
|
+
raise ValueError(f"memory backup not found: {item_id}")
|
|
505
|
+
manifest = load_backup_manifest(manifest_path)
|
|
506
|
+
if not manifest:
|
|
507
|
+
raise ValueError(f"invalid memory backup manifest: {item_id}")
|
|
508
|
+
return root, manifest
|
|
509
|
+
|
|
510
|
+
|
|
511
|
+
def load_backup_manifest(path: Path) -> dict[str, Any] | None:
|
|
512
|
+
try:
|
|
513
|
+
payload = json.loads(path.read_text(encoding="utf-8"))
|
|
514
|
+
except (OSError, json.JSONDecodeError):
|
|
515
|
+
return None
|
|
516
|
+
return payload if isinstance(payload, dict) else None
|
|
517
|
+
|
|
518
|
+
|
|
519
|
+
def passphrase_from_env(passphrase_env: str | None) -> str:
|
|
520
|
+
env_name = passphrase_env or "AGENT_DEVKIT_BACKUP_PASSPHRASE"
|
|
521
|
+
if not re.match(r"^[A-Za-z_][A-Za-z0-9_]*$", env_name):
|
|
522
|
+
raise ValueError("--passphrase-env must be an environment variable name")
|
|
523
|
+
value = os.environ.get(env_name)
|
|
524
|
+
if not value:
|
|
525
|
+
raise ValueError(f"memory backup passphrase environment variable is not set: {env_name}")
|
|
526
|
+
if len(value) < 8:
|
|
527
|
+
raise ValueError("memory backup passphrase must have at least 8 characters")
|
|
528
|
+
return value
|
|
529
|
+
|
|
530
|
+
|
|
531
|
+
def write_encrypted_backup_package(package_path: Path, memory_dir: Path, manifest: dict[str, Any], passphrase: str) -> None:
|
|
532
|
+
tar_bytes = memory_tar_bytes(memory_dir)
|
|
533
|
+
salt = os.urandom(16)
|
|
534
|
+
nonce = os.urandom(16)
|
|
535
|
+
key = derive_backup_key(passphrase, salt)
|
|
536
|
+
ciphertext = xor_bytes(tar_bytes, key, nonce)
|
|
537
|
+
header = {
|
|
538
|
+
"schema_version": BACKUP_PACKAGE_SCHEMA_VERSION,
|
|
539
|
+
"algorithm": BACKUP_PACKAGE_ALGORITHM,
|
|
540
|
+
"kdf": "PBKDF2-HMAC-SHA256",
|
|
541
|
+
"iterations": 200_000,
|
|
542
|
+
"salt": base64.b64encode(salt).decode("ascii"),
|
|
543
|
+
"nonce": base64.b64encode(nonce).decode("ascii"),
|
|
544
|
+
"manifest": {key: value for key, value in manifest.items() if key != "files"},
|
|
545
|
+
"files": manifest.get("files") or [],
|
|
546
|
+
}
|
|
547
|
+
header_bytes = json.dumps(header, ensure_ascii=False, sort_keys=True).encode("utf-8")
|
|
548
|
+
tag = hmac.new(key, header_bytes + ciphertext, hashlib.sha256).hexdigest()
|
|
549
|
+
envelope = {
|
|
550
|
+
"schema_version": BACKUP_PACKAGE_SCHEMA_VERSION,
|
|
551
|
+
"header": header,
|
|
552
|
+
"ciphertext": base64.b64encode(ciphertext).decode("ascii"),
|
|
553
|
+
"tag": tag,
|
|
554
|
+
}
|
|
555
|
+
package_path.write_text(json.dumps(envelope, ensure_ascii=False, indent=2, sort_keys=True) + "\n", encoding="utf-8")
|
|
556
|
+
|
|
557
|
+
|
|
558
|
+
def read_encrypted_backup_header(package_path: Path, *, passphrase_env: str | None = None) -> dict[str, Any]:
|
|
559
|
+
envelope = read_backup_package(package_path)
|
|
560
|
+
header = envelope["header"]
|
|
561
|
+
manifest = header.get("manifest") if isinstance(header.get("manifest"), dict) else {}
|
|
562
|
+
payload = dict(manifest)
|
|
563
|
+
payload["files"] = header.get("files") if isinstance(header.get("files"), list) else []
|
|
564
|
+
payload["encrypted"] = True
|
|
565
|
+
payload["sensitive_local_copy"] = False
|
|
566
|
+
payload["package"] = str(package_path)
|
|
567
|
+
payload["package_name"] = package_path.name
|
|
568
|
+
payload.setdefault("id", package_path.stem)
|
|
569
|
+
payload.setdefault("title", package_path.stem)
|
|
570
|
+
return payload
|
|
571
|
+
|
|
572
|
+
|
|
573
|
+
def extract_encrypted_backup_package(package_path: Path, target: Path, passphrase: str) -> None:
|
|
574
|
+
envelope = read_backup_package(package_path)
|
|
575
|
+
header = envelope["header"]
|
|
576
|
+
salt = base64.b64decode(header["salt"])
|
|
577
|
+
nonce = base64.b64decode(header["nonce"])
|
|
578
|
+
ciphertext = base64.b64decode(envelope["ciphertext"])
|
|
579
|
+
key = derive_backup_key(passphrase, salt)
|
|
580
|
+
header_bytes = json.dumps(header, ensure_ascii=False, sort_keys=True).encode("utf-8")
|
|
581
|
+
expected_tag = hmac.new(key, header_bytes + ciphertext, hashlib.sha256).hexdigest()
|
|
582
|
+
if not hmac.compare_digest(expected_tag, str(envelope.get("tag") or "")):
|
|
583
|
+
raise ValueError("memory backup package integrity check failed")
|
|
584
|
+
tar_bytes = xor_bytes(ciphertext, key, nonce)
|
|
585
|
+
extract_memory_tar(tar_bytes, target)
|
|
586
|
+
|
|
587
|
+
|
|
588
|
+
def read_backup_package(package_path: Path) -> dict[str, Any]:
|
|
589
|
+
if not package_path.exists():
|
|
590
|
+
raise ValueError(f"memory backup package not found: {package_path}")
|
|
591
|
+
try:
|
|
592
|
+
envelope = json.loads(package_path.read_text(encoding="utf-8"))
|
|
593
|
+
except (OSError, json.JSONDecodeError) as exc:
|
|
594
|
+
raise ValueError(f"invalid memory backup package: {package_path}") from exc
|
|
595
|
+
if not isinstance(envelope, dict) or envelope.get("schema_version") != BACKUP_PACKAGE_SCHEMA_VERSION:
|
|
596
|
+
raise ValueError(f"unsupported memory backup package: {package_path}")
|
|
597
|
+
header = envelope.get("header")
|
|
598
|
+
if not isinstance(header, dict) or header.get("algorithm") != BACKUP_PACKAGE_ALGORITHM:
|
|
599
|
+
raise ValueError(f"unsupported memory backup package algorithm: {package_path}")
|
|
600
|
+
return envelope
|
|
601
|
+
|
|
602
|
+
|
|
603
|
+
def memory_tar_bytes(memory_dir: Path) -> bytes:
|
|
604
|
+
buffer = io.BytesIO()
|
|
605
|
+
with tarfile.open(fileobj=buffer, mode="w:gz") as archive:
|
|
606
|
+
for path in sorted(item for item in memory_dir.rglob("*") if item.is_file()):
|
|
607
|
+
archive.add(path, arcname=str(Path("memory") / path.relative_to(memory_dir)))
|
|
608
|
+
return buffer.getvalue()
|
|
609
|
+
|
|
610
|
+
|
|
611
|
+
def extract_memory_tar(payload: bytes, target: Path) -> None:
|
|
612
|
+
target.mkdir(parents=True, exist_ok=True)
|
|
613
|
+
with tarfile.open(fileobj=io.BytesIO(payload), mode="r:gz") as archive:
|
|
614
|
+
for member in archive.getmembers():
|
|
615
|
+
member_path = Path(member.name)
|
|
616
|
+
if member_path.is_absolute() or ".." in member_path.parts or not member.name.startswith("memory/"):
|
|
617
|
+
raise ValueError(f"unsafe path in memory backup package: {member.name}")
|
|
618
|
+
if not member.isfile():
|
|
619
|
+
continue
|
|
620
|
+
output = target / member_path
|
|
621
|
+
output.parent.mkdir(parents=True, exist_ok=True)
|
|
622
|
+
extracted = archive.extractfile(member)
|
|
623
|
+
if extracted is None:
|
|
624
|
+
continue
|
|
625
|
+
output.write_bytes(extracted.read())
|
|
626
|
+
|
|
627
|
+
|
|
628
|
+
def derive_backup_key(passphrase: str, salt: bytes) -> bytes:
|
|
629
|
+
return hashlib.pbkdf2_hmac("sha256", passphrase.encode("utf-8"), salt, 200_000, dklen=32)
|
|
630
|
+
|
|
631
|
+
|
|
632
|
+
def xor_bytes(payload: bytes, key: bytes, nonce: bytes) -> bytes:
|
|
633
|
+
output = bytearray()
|
|
634
|
+
counter = 0
|
|
635
|
+
for index in range(0, len(payload), 32):
|
|
636
|
+
block = payload[index : index + 32]
|
|
637
|
+
stream = hmac.new(key, nonce + counter.to_bytes(8, "big"), hashlib.sha256).digest()
|
|
638
|
+
output.extend(byte ^ stream[offset] for offset, byte in enumerate(block))
|
|
639
|
+
counter += 1
|
|
640
|
+
return bytes(output)
|
|
641
|
+
|
|
642
|
+
|
|
643
|
+
def package_path_from_manifest(backup_root: Path, manifest: dict[str, Any]) -> Path | None:
|
|
644
|
+
raw = manifest.get("package")
|
|
645
|
+
if isinstance(raw, str) and raw:
|
|
646
|
+
return Path(raw).expanduser().resolve()
|
|
647
|
+
package_name = manifest.get("package_name")
|
|
648
|
+
if isinstance(package_name, str) and package_name:
|
|
649
|
+
return backup_root / package_name
|
|
650
|
+
candidates = sorted(backup_root.glob("*.adkmb"))
|
|
651
|
+
return candidates[0] if candidates else None
|
|
652
|
+
|
|
653
|
+
|
|
249
654
|
def increment_bucket(bucket: dict[str, Any], key: str, now: str) -> None:
|
|
250
655
|
item = bucket.setdefault(key, {"count": 0, "first_seen": now, "last_seen": now})
|
|
251
656
|
item["count"] = int(item.get("count") or 0) + 1
|
|
@@ -100,10 +100,12 @@ def setup_mini_brain(
|
|
|
100
100
|
) -> dict[str, Any]:
|
|
101
101
|
if dry_run or not yes:
|
|
102
102
|
status = "planned" if dry_run else "needs-confirmation"
|
|
103
|
+
needs_confirmation = not dry_run and not yes
|
|
103
104
|
return {
|
|
104
105
|
"kind": "mini-brain-setup",
|
|
105
106
|
"status": status,
|
|
106
107
|
"ok": bool(dry_run),
|
|
108
|
+
"exit_code": 2 if needs_confirmation else 0,
|
|
107
109
|
"dry_run": dry_run,
|
|
108
110
|
"yes": yes,
|
|
109
111
|
"stored_secret": False,
|
|
@@ -114,11 +116,19 @@ def setup_mini_brain(
|
|
|
114
116
|
}
|
|
115
117
|
|
|
116
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)
|
|
117
126
|
if not pull.get("ok"):
|
|
118
|
-
|
|
127
|
+
payload = {
|
|
119
128
|
"kind": "mini-brain-setup",
|
|
120
129
|
"status": "failed",
|
|
121
130
|
"ok": False,
|
|
131
|
+
"exit_code": int(pull.get("exit_code") or 2),
|
|
122
132
|
"dry_run": False,
|
|
123
133
|
"yes": True,
|
|
124
134
|
"stored_secret": False,
|
|
@@ -127,6 +137,14 @@ def setup_mini_brain(
|
|
|
127
137
|
"next_steps": ["Install Ollama or run agent ollama pull qwen3:0.6b --yes"],
|
|
128
138
|
"message": pull.get("message") or "Could not pull the mini-brain model.",
|
|
129
139
|
}
|
|
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
|
|
130
148
|
|
|
131
149
|
existing_config = load_config()
|
|
132
150
|
existing_ollama = (
|
|
@@ -155,6 +173,7 @@ def setup_mini_brain(
|
|
|
155
173
|
"config_path": str(written_path),
|
|
156
174
|
"mini_brain": contract,
|
|
157
175
|
"pull": pull,
|
|
176
|
+
"toolchain_install": toolchain_install,
|
|
158
177
|
"llm_configure": configured,
|
|
159
178
|
"next_steps": ["Use low-risk setup, wizard and summary prompts normally."],
|
|
160
179
|
}
|
|
@@ -3,9 +3,11 @@
|
|
|
3
3
|
from __future__ import annotations
|
|
4
4
|
|
|
5
5
|
import argparse
|
|
6
|
+
import re
|
|
6
7
|
from pathlib import Path
|
|
7
8
|
from typing import Any
|
|
8
9
|
|
|
10
|
+
from cli.aikit.agent_registry import load_agent_registry
|
|
9
11
|
from cli.aikit.agent_executor import execute_plan_tasks
|
|
10
12
|
from cli.aikit.capability_runtime import load_agent, run_capability
|
|
11
13
|
from cli.aikit.calendar import calendar_summary, calendar_today, calendar_tomorrow
|
|
@@ -22,7 +24,7 @@ from cli.aikit.memory import napkin_context, record_usage
|
|
|
22
24
|
from cli.aikit.model_router import build_model_plan
|
|
23
25
|
from cli.aikit.module_controller import run_module_controller
|
|
24
26
|
from cli.aikit.orchestrator import attach_source_to_primary_task, build_execution_plan, mark_plan_after_execution, mark_review_task
|
|
25
|
-
from cli.aikit.personality import load_personality
|
|
27
|
+
from cli.aikit.personality import load_personality, update_personality
|
|
26
28
|
from cli.aikit.provider_wizard import missing_source_wizard
|
|
27
29
|
from cli.aikit.review_gate import build_review_gate
|
|
28
30
|
from cli.aikit.router import route_prompt
|
|
@@ -36,6 +38,106 @@ def effective_dry_run(args: argparse.Namespace) -> bool:
|
|
|
36
38
|
return bool(getattr(args, "dry_run", False) or getattr(args, "global_dry_run", False))
|
|
37
39
|
|
|
38
40
|
|
|
41
|
+
def parse_rename_prompt(prompt: str) -> str | None:
|
|
42
|
+
text = " ".join(prompt.strip().split())
|
|
43
|
+
patterns = [
|
|
44
|
+
r"(?i)\b(?:mude|troque|altere|renomeie)\s+(?:o\s+)?seu\s+nome\s+para\s+(.+)$",
|
|
45
|
+
r"(?i)\b(?:seu\s+nome\s+agora\s+e|seu\s+nome\s+agora\s+é)\s+(.+)$",
|
|
46
|
+
r"(?i)\b(?:teu\s+nome\s+agora\s+e|teu\s+nome\s+agora\s+é)\s+(.+)$",
|
|
47
|
+
]
|
|
48
|
+
for pattern in patterns:
|
|
49
|
+
match = re.search(pattern, text)
|
|
50
|
+
if not match:
|
|
51
|
+
continue
|
|
52
|
+
value = clean_requested_name(match.group(1))
|
|
53
|
+
if value:
|
|
54
|
+
return value
|
|
55
|
+
return None
|
|
56
|
+
|
|
57
|
+
|
|
58
|
+
def clean_requested_name(value: str) -> str | None:
|
|
59
|
+
cleaned = " ".join(value.strip().strip("\"'`.,;:!").split())
|
|
60
|
+
if not cleaned:
|
|
61
|
+
return None
|
|
62
|
+
return cleaned[:80]
|
|
63
|
+
|
|
64
|
+
|
|
65
|
+
def is_capabilities_help_prompt(prompt: str) -> bool:
|
|
66
|
+
normalized = normalize_text(prompt)
|
|
67
|
+
if normalized in {"ajuda", "help", "o que voce faz", "o que voce consegue fazer"}:
|
|
68
|
+
return True
|
|
69
|
+
capability_markers = (
|
|
70
|
+
"o que voce consegue fazer",
|
|
71
|
+
"o que voce pode fazer",
|
|
72
|
+
"como voce pode ajudar",
|
|
73
|
+
"como usar o agent",
|
|
74
|
+
"como usar voce",
|
|
75
|
+
"quais suas capacidades",
|
|
76
|
+
"quais sao suas capacidades",
|
|
77
|
+
"quais agentes voce tem",
|
|
78
|
+
"que agentes voce tem",
|
|
79
|
+
)
|
|
80
|
+
return any(marker in normalized for marker in capability_markers)
|
|
81
|
+
|
|
82
|
+
|
|
83
|
+
def normalize_text(value: str) -> str:
|
|
84
|
+
replacements = str.maketrans(
|
|
85
|
+
{
|
|
86
|
+
"á": "a",
|
|
87
|
+
"à": "a",
|
|
88
|
+
"ã": "a",
|
|
89
|
+
"â": "a",
|
|
90
|
+
"é": "e",
|
|
91
|
+
"ê": "e",
|
|
92
|
+
"í": "i",
|
|
93
|
+
"ó": "o",
|
|
94
|
+
"õ": "o",
|
|
95
|
+
"ô": "o",
|
|
96
|
+
"ú": "u",
|
|
97
|
+
"ç": "c",
|
|
98
|
+
}
|
|
99
|
+
)
|
|
100
|
+
text = value.lower().translate(replacements)
|
|
101
|
+
return " ".join(re.sub(r"[^a-z0-9._:-]+", " ", text).split())
|
|
102
|
+
|
|
103
|
+
|
|
104
|
+
def local_capabilities_help_response(prompt: str, *, name: str) -> dict[str, Any]:
|
|
105
|
+
registry = load_agent_registry(ROOT)
|
|
106
|
+
agents = registry.get("agents") if isinstance(registry.get("agents"), dict) else {}
|
|
107
|
+
capabilities = registry.get("capabilities") if isinstance(registry.get("capabilities"), dict) else {}
|
|
108
|
+
examples = [
|
|
109
|
+
'agent "analise o card 7914 do projeto sustentacao no azure"',
|
|
110
|
+
'agent "mude seu nome para Ianota"',
|
|
111
|
+
"agent onboard minimal",
|
|
112
|
+
"agent catalog search pr",
|
|
113
|
+
"agent mcp tools",
|
|
114
|
+
"agent memory show",
|
|
115
|
+
]
|
|
116
|
+
response = (
|
|
117
|
+
f"Eu sou {name}. Posso operar como harness/agente local do Agent DevKit: "
|
|
118
|
+
"validar onboarding, lembrar preferencias locais, rotear pedidos para agentes especialistas, "
|
|
119
|
+
"criar wizards de configuracao quando faltar provider/source, executar capabilities deterministicas, "
|
|
120
|
+
"expor ferramentas via MCP, preparar automacoes locais e usar LLMs configuradas quando a tarefa exigir raciocinio aberto. "
|
|
121
|
+
f"Neste runtime encontrei {len(agents)} agentes e {len(capabilities)} capabilities."
|
|
122
|
+
)
|
|
123
|
+
return {
|
|
124
|
+
"kind": "agent",
|
|
125
|
+
"status": "ok",
|
|
126
|
+
"ok": True,
|
|
127
|
+
"requires_llm": False,
|
|
128
|
+
"prompt_received": True,
|
|
129
|
+
"prompt_length": len(prompt),
|
|
130
|
+
"mode": "local-capabilities-help",
|
|
131
|
+
"identity": {"name": name, "source": "local"},
|
|
132
|
+
"response": response,
|
|
133
|
+
"catalog": {
|
|
134
|
+
"agents": len(agents),
|
|
135
|
+
"capabilities": len(capabilities),
|
|
136
|
+
},
|
|
137
|
+
"next_steps": examples,
|
|
138
|
+
}
|
|
139
|
+
|
|
140
|
+
|
|
39
141
|
def agent_requires_llm(args: argparse.Namespace) -> dict[str, Any]:
|
|
40
142
|
prompt = " ".join(args.prompt).strip()
|
|
41
143
|
return run_agent_prompt_request(
|
|
@@ -70,6 +172,21 @@ def run_agent_prompt_request(request: AgentPromptRequest) -> dict[str, Any]:
|
|
|
70
172
|
raise DevKitError(str(exc)) from exc
|
|
71
173
|
personality = load_personality()
|
|
72
174
|
name = public_name(personality=personality, invoked_as=request.prog_name)
|
|
175
|
+
rename = parse_rename_prompt(prompt)
|
|
176
|
+
if rename:
|
|
177
|
+
updated = update_personality(agent_name=rename)
|
|
178
|
+
result = {
|
|
179
|
+
"kind": "agent",
|
|
180
|
+
"status": "ok",
|
|
181
|
+
"ok": True,
|
|
182
|
+
"requires_llm": False,
|
|
183
|
+
"prompt_received": True,
|
|
184
|
+
"prompt_length": len(prompt),
|
|
185
|
+
"identity": {"name": updated.get("agent_name"), "source": "local"},
|
|
186
|
+
"action": "rename",
|
|
187
|
+
"response": f"Pronto. Meu nome local agora e {updated.get('agent_name')}.",
|
|
188
|
+
}
|
|
189
|
+
return finalize_agent_session(result, session, prompt, backend=request.llm)
|
|
73
190
|
if is_identity_question(prompt):
|
|
74
191
|
result = {
|
|
75
192
|
"kind": "agent",
|
|
@@ -82,6 +199,13 @@ def run_agent_prompt_request(request: AgentPromptRequest) -> dict[str, Any]:
|
|
|
82
199
|
"response": local_identity_response(prompt, name=name),
|
|
83
200
|
}
|
|
84
201
|
return finalize_agent_session(result, session, prompt, backend=request.llm)
|
|
202
|
+
if is_capabilities_help_prompt(prompt):
|
|
203
|
+
return finalize_agent_session(
|
|
204
|
+
local_capabilities_help_response(prompt, name=name),
|
|
205
|
+
session,
|
|
206
|
+
prompt,
|
|
207
|
+
backend=request.llm,
|
|
208
|
+
)
|
|
85
209
|
natural_result = dispatch_natural_operational_prompt(prompt)
|
|
86
210
|
if natural_result:
|
|
87
211
|
return finalize_agent_session(natural_result, session, prompt, backend=request.llm)
|