devops-bot-sdk 1.4.118__tar.gz → 1.4.119__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- {devops_bot_sdk-1.4.118 → devops_bot_sdk-1.4.119}/PKG-INFO +1 -1
- {devops_bot_sdk-1.4.118 → devops_bot_sdk-1.4.119}/devops_bot_sdk.egg-info/PKG-INFO +1 -1
- {devops_bot_sdk-1.4.118 → devops_bot_sdk-1.4.119}/devops_bot_sdk.egg-info/SOURCES.txt +0 -1
- {devops_bot_sdk-1.4.118 → devops_bot_sdk-1.4.119}/sdk/__init__.py +2 -2
- {devops_bot_sdk-1.4.118 → devops_bot_sdk-1.4.119}/sdk/agentd.py +6 -7
- {devops_bot_sdk-1.4.118 → devops_bot_sdk-1.4.119}/sdk/cli.py +25 -263
- {devops_bot_sdk-1.4.118 → devops_bot_sdk-1.4.119}/sdk/client.py +8 -101
- {devops_bot_sdk-1.4.118 → devops_bot_sdk-1.4.119}/sdk/ipc/handlers.py +8 -8
- {devops_bot_sdk-1.4.118 → devops_bot_sdk-1.4.119}/sdk/ui/api.py +31 -228
- {devops_bot_sdk-1.4.118 → devops_bot_sdk-1.4.119}/sdk/ui/static/app.js +17 -35
- {devops_bot_sdk-1.4.118 → devops_bot_sdk-1.4.119}/sdk/ui/static/custom-agents.html +7 -8
- {devops_bot_sdk-1.4.118 → devops_bot_sdk-1.4.119}/sdk/ui/static/default-agents.html +3 -3
- devops_bot_sdk-1.4.118/sdk/ui/static/prompt-studio.html +0 -92
- {devops_bot_sdk-1.4.118 → devops_bot_sdk-1.4.119}/README.md +0 -0
- {devops_bot_sdk-1.4.118 → devops_bot_sdk-1.4.119}/devops_bot_sdk.egg-info/dependency_links.txt +0 -0
- {devops_bot_sdk-1.4.118 → devops_bot_sdk-1.4.119}/devops_bot_sdk.egg-info/entry_points.txt +0 -0
- {devops_bot_sdk-1.4.118 → devops_bot_sdk-1.4.119}/devops_bot_sdk.egg-info/requires.txt +0 -0
- {devops_bot_sdk-1.4.118 → devops_bot_sdk-1.4.119}/devops_bot_sdk.egg-info/top_level.txt +0 -0
- {devops_bot_sdk-1.4.118 → devops_bot_sdk-1.4.119}/pyproject.toml +0 -0
- {devops_bot_sdk-1.4.118 → devops_bot_sdk-1.4.119}/sdk/agents/__init__.py +0 -0
- {devops_bot_sdk-1.4.118 → devops_bot_sdk-1.4.119}/sdk/agents/builder.py +0 -0
- {devops_bot_sdk-1.4.118 → devops_bot_sdk-1.4.119}/sdk/agents/chat.py +0 -0
- {devops_bot_sdk-1.4.118 → devops_bot_sdk-1.4.119}/sdk/agents/connectors.py +0 -0
- {devops_bot_sdk-1.4.118 → devops_bot_sdk-1.4.119}/sdk/agents/engines/__init__.py +0 -0
- {devops_bot_sdk-1.4.118 → devops_bot_sdk-1.4.119}/sdk/agents/engines/claude_code.py +0 -0
- {devops_bot_sdk-1.4.118 → devops_bot_sdk-1.4.119}/sdk/agents/engines/http.py +0 -0
- {devops_bot_sdk-1.4.118 → devops_bot_sdk-1.4.119}/sdk/agents/engines/mcp.py +0 -0
- {devops_bot_sdk-1.4.118 → devops_bot_sdk-1.4.119}/sdk/agents/engines/n8n.py +0 -0
- {devops_bot_sdk-1.4.118 → devops_bot_sdk-1.4.119}/sdk/agents/engines/shell.py +0 -0
- {devops_bot_sdk-1.4.118 → devops_bot_sdk-1.4.119}/sdk/agents/integrations.py +0 -0
- {devops_bot_sdk-1.4.118 → devops_bot_sdk-1.4.119}/sdk/agents/inventory.py +0 -0
- {devops_bot_sdk-1.4.118 → devops_bot_sdk-1.4.119}/sdk/agents/library/__init__.py +0 -0
- {devops_bot_sdk-1.4.118 → devops_bot_sdk-1.4.119}/sdk/agents/manifest.py +0 -0
- {devops_bot_sdk-1.4.118 → devops_bot_sdk-1.4.119}/sdk/agents/prompt_studio.py +0 -0
- {devops_bot_sdk-1.4.118 → devops_bot_sdk-1.4.119}/sdk/agents/prompt_template.py +0 -0
- {devops_bot_sdk-1.4.118 → devops_bot_sdk-1.4.119}/sdk/agents/prompt_vars.py +0 -0
- {devops_bot_sdk-1.4.118 → devops_bot_sdk-1.4.119}/sdk/agents/run_log.py +0 -0
- {devops_bot_sdk-1.4.118 → devops_bot_sdk-1.4.119}/sdk/agents/secrets.py +0 -0
- {devops_bot_sdk-1.4.118 → devops_bot_sdk-1.4.119}/sdk/agents/usage.py +0 -0
- {devops_bot_sdk-1.4.118 → devops_bot_sdk-1.4.119}/sdk/agents/varstore.py +0 -0
- {devops_bot_sdk-1.4.118 → devops_bot_sdk-1.4.119}/sdk/collectors/__init__.py +0 -0
- {devops_bot_sdk-1.4.118 → devops_bot_sdk-1.4.119}/sdk/collectors/files.py +0 -0
- {devops_bot_sdk-1.4.118 → devops_bot_sdk-1.4.119}/sdk/collectors/process.py +0 -0
- {devops_bot_sdk-1.4.118 → devops_bot_sdk-1.4.119}/sdk/collectors/screenshot.py +0 -0
- {devops_bot_sdk-1.4.118 → devops_bot_sdk-1.4.119}/sdk/config.py +0 -0
- {devops_bot_sdk-1.4.118 → devops_bot_sdk-1.4.119}/sdk/crucial.py +0 -0
- {devops_bot_sdk-1.4.118 → devops_bot_sdk-1.4.119}/sdk/design_verify/__init__.py +0 -0
- {devops_bot_sdk-1.4.118 → devops_bot_sdk-1.4.119}/sdk/design_verify/assets.py +0 -0
- {devops_bot_sdk-1.4.118 → devops_bot_sdk-1.4.119}/sdk/design_verify/bootstrap.py +0 -0
- {devops_bot_sdk-1.4.118 → devops_bot_sdk-1.4.119}/sdk/design_verify/browser.py +0 -0
- {devops_bot_sdk-1.4.118 → devops_bot_sdk-1.4.119}/sdk/design_verify/compare.py +0 -0
- {devops_bot_sdk-1.4.118 → devops_bot_sdk-1.4.119}/sdk/design_verify/loop.py +0 -0
- {devops_bot_sdk-1.4.118 → devops_bot_sdk-1.4.119}/sdk/exceptions.py +0 -0
- {devops_bot_sdk-1.4.118 → devops_bot_sdk-1.4.119}/sdk/git_ops.py +0 -0
- {devops_bot_sdk-1.4.118 → devops_bot_sdk-1.4.119}/sdk/graphify.py +0 -0
- {devops_bot_sdk-1.4.118 → devops_bot_sdk-1.4.119}/sdk/hooks/__init__.py +0 -0
- {devops_bot_sdk-1.4.118 → devops_bot_sdk-1.4.119}/sdk/hooks/crucial_guard.py +0 -0
- {devops_bot_sdk-1.4.118 → devops_bot_sdk-1.4.119}/sdk/ipc/__init__.py +0 -0
- {devops_bot_sdk-1.4.118 → devops_bot_sdk-1.4.119}/sdk/ipc/electron_bridge.py +0 -0
- {devops_bot_sdk-1.4.118 → devops_bot_sdk-1.4.119}/sdk/local_exec.py +0 -0
- {devops_bot_sdk-1.4.118 → devops_bot_sdk-1.4.119}/sdk/models/__init__.py +0 -0
- {devops_bot_sdk-1.4.118 → devops_bot_sdk-1.4.119}/sdk/models/envelope.py +0 -0
- {devops_bot_sdk-1.4.118 → devops_bot_sdk-1.4.119}/sdk/models/requests.py +0 -0
- {devops_bot_sdk-1.4.118 → devops_bot_sdk-1.4.119}/sdk/models/responses.py +0 -0
- {devops_bot_sdk-1.4.118 → devops_bot_sdk-1.4.119}/sdk/models/snapshots.py +0 -0
- {devops_bot_sdk-1.4.118 → devops_bot_sdk-1.4.119}/sdk/platform_compat.py +0 -0
- {devops_bot_sdk-1.4.118 → devops_bot_sdk-1.4.119}/sdk/py.typed +0 -0
- {devops_bot_sdk-1.4.118 → devops_bot_sdk-1.4.119}/sdk/run_auto.py +0 -0
- {devops_bot_sdk-1.4.118 → devops_bot_sdk-1.4.119}/sdk/sse.py +0 -0
- {devops_bot_sdk-1.4.118 → devops_bot_sdk-1.4.119}/sdk/test.py +0 -0
- {devops_bot_sdk-1.4.118 → devops_bot_sdk-1.4.119}/sdk/test_pipeline.py +0 -0
- {devops_bot_sdk-1.4.118 → devops_bot_sdk-1.4.119}/sdk/ui/__init__.py +0 -0
- {devops_bot_sdk-1.4.118 → devops_bot_sdk-1.4.119}/sdk/ui/dashboard.py +0 -0
- {devops_bot_sdk-1.4.118 → devops_bot_sdk-1.4.119}/sdk/ui/ip_allowlist.py +0 -0
- {devops_bot_sdk-1.4.118 → devops_bot_sdk-1.4.119}/sdk/ui/jobs.py +0 -0
- {devops_bot_sdk-1.4.118 → devops_bot_sdk-1.4.119}/sdk/ui/runner.py +0 -0
- {devops_bot_sdk-1.4.118 → devops_bot_sdk-1.4.119}/sdk/ui/security.py +0 -0
- {devops_bot_sdk-1.4.118 → devops_bot_sdk-1.4.119}/sdk/ui/server.py +0 -0
- {devops_bot_sdk-1.4.118 → devops_bot_sdk-1.4.119}/sdk/ui/service.py +0 -0
- {devops_bot_sdk-1.4.118 → devops_bot_sdk-1.4.119}/sdk/ui/setup_manager.py +0 -0
- {devops_bot_sdk-1.4.118 → devops_bot_sdk-1.4.119}/sdk/ui/static/agents.html +0 -0
- {devops_bot_sdk-1.4.118 → devops_bot_sdk-1.4.119}/sdk/ui/static/index.html +0 -0
- {devops_bot_sdk-1.4.118 → devops_bot_sdk-1.4.119}/sdk/ui/static/runs.html +0 -0
- {devops_bot_sdk-1.4.118 → devops_bot_sdk-1.4.119}/sdk/ui/static/setup.html +0 -0
- {devops_bot_sdk-1.4.118 → devops_bot_sdk-1.4.119}/sdk/ui/static/styles.css +0 -0
- {devops_bot_sdk-1.4.118 → devops_bot_sdk-1.4.119}/sdk/ui/static/tickets.html +0 -0
- {devops_bot_sdk-1.4.118 → devops_bot_sdk-1.4.119}/sdk/updater.py +0 -0
- {devops_bot_sdk-1.4.118 → devops_bot_sdk-1.4.119}/setup.cfg +0 -0
- {devops_bot_sdk-1.4.118 → devops_bot_sdk-1.4.119}/tests/test_agent_chat_features.py +0 -0
- {devops_bot_sdk-1.4.118 → devops_bot_sdk-1.4.119}/tests/test_agents_connectors_builder.py +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
"""AgentOS Desktop SDK — thin HTTPS/SSE client for the Electron app.
|
|
2
2
|
|
|
3
|
-
Version: 1.4.
|
|
3
|
+
Version: 1.4.119
|
|
4
4
|
|
|
5
5
|
Public surface:
|
|
6
6
|
BackendClient.from_config() — create client from ~/.agentos/config.toml
|
|
@@ -30,7 +30,7 @@ Rules:
|
|
|
30
30
|
- All data egress through submit_webhook only
|
|
31
31
|
"""
|
|
32
32
|
|
|
33
|
-
__version__ = "1.4.
|
|
33
|
+
__version__ = "1.4.119" # SINGLE SOURCE OF TRUTH — bump on every change; pyproject,
|
|
34
34
|
__author__ = "AgentOS" # sdk.client.SDK_VERSION and the UI server version all read this.
|
|
35
35
|
|
|
36
36
|
from sdk.client import BackendClient
|
|
@@ -15,9 +15,9 @@ See docs/GUIDE_2_Development_Plan_Custom_Agents.md:
|
|
|
15
15
|
``sdk/run_auto.py`` (which only ever drives the Jira To-Do queue) into a
|
|
16
16
|
general-purpose daemon with five components:
|
|
17
17
|
1. manifest loader — ``sdk.agents.manifest.load_agents()``
|
|
18
|
-
2. registration
|
|
19
|
-
backend
|
|
20
|
-
|
|
18
|
+
2. (removed) registration — the daemon no longer pushes local manifests to
|
|
19
|
+
the backend; agents are authored in the web app
|
|
20
|
+
and the SDK is consume-only (local trigger/run).
|
|
21
21
|
3. department pull — fetch this user's DEPARTMENT default agents
|
|
22
22
|
(``BackendClient.list_department_agents()``)
|
|
23
23
|
and materialize them alongside personal
|
|
@@ -451,12 +451,11 @@ class AgentDaemon:
|
|
|
451
451
|
department = await self._load_department_agents()
|
|
452
452
|
self._manifests = personal + department
|
|
453
453
|
|
|
454
|
+
# Consume-only: the daemon no longer registers/pushes local manifests to
|
|
455
|
+
# the backend — agents are authored in the web app and written to the
|
|
456
|
+
# `agents` table. The daemon just schedules + runs them LOCALLY on trigger.
|
|
454
457
|
for m in personal:
|
|
455
458
|
self._schedule_if_cron(m, job_id=m.name)
|
|
456
|
-
try:
|
|
457
|
-
await self.client.register_custom_agent(m.model_dump(mode="json"))
|
|
458
|
-
except Exception as exc: # noqa: BLE001 — registration is best-effort
|
|
459
|
-
logger.warning("agentd.register_failed agent=%s err=%s", m.name, exc)
|
|
460
459
|
|
|
461
460
|
for m in department:
|
|
462
461
|
# "dept:" job-id prefix avoids colliding with a personal agent
|
|
@@ -104,10 +104,10 @@ def _print_usage() -> None:
|
|
|
104
104
|
print("usage: agentos <configure|agent|integration|default-agents|agentd> ...")
|
|
105
105
|
print()
|
|
106
106
|
print(" agentos configure [--rotate]")
|
|
107
|
-
print(" agentos agent <
|
|
107
|
+
print(" agentos agent <list|run NAME|chat NAME MSG|logs NAME|enable NAME|disable NAME|rm NAME>")
|
|
108
108
|
print(" agentos agent <set-key NAME KEY[=VALUE]…|keys NAME|rm-key NAME KEY…> # persistent API keys (custom agents)")
|
|
109
109
|
print(" agentos integration <add PATH|test NAME|list|rm NAME>")
|
|
110
|
-
print(" agentos default-agents
|
|
110
|
+
print(" agentos default-agents list [--department-id UUID] # read-only; agents are created in the web app")
|
|
111
111
|
print(" agentos agentd <start [--foreground]|status>")
|
|
112
112
|
|
|
113
113
|
|
|
@@ -117,11 +117,13 @@ def _agent_command(args: list[str]) -> None:
|
|
|
117
117
|
verb = args[0] if args else None
|
|
118
118
|
rest = args[1:]
|
|
119
119
|
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
if verb
|
|
124
|
-
|
|
120
|
+
# Agent CREATION (`new` / `add`) is not owned by the SDK anymore — agents are
|
|
121
|
+
# authored in the web app and written to the backend `agents` table. The CLI is
|
|
122
|
+
# consume-only: list / run / chat / logs / enable / disable / rm / keys.
|
|
123
|
+
if verb in ("new", "add"):
|
|
124
|
+
print("`agentos agent new/add` has been removed — agents are created in the "
|
|
125
|
+
"web app now. Use `agentos agent list` to see yours and `agentos agent "
|
|
126
|
+
"run <name>` to run one.")
|
|
125
127
|
return
|
|
126
128
|
if verb == "list":
|
|
127
129
|
_agent_list()
|
|
@@ -132,10 +134,6 @@ def _agent_command(args: list[str]) -> None:
|
|
|
132
134
|
if verb == "chat" and len(rest) >= 2:
|
|
133
135
|
_agent_chat(rest[0], " ".join(rest[1:]))
|
|
134
136
|
return
|
|
135
|
-
if verb == "template":
|
|
136
|
-
from sdk.agents.prompt_template import render_system_prompt
|
|
137
|
-
print(render_system_prompt(name=(rest[0] if rest else "my-agent")))
|
|
138
|
-
return
|
|
139
137
|
if verb == "logs" and rest:
|
|
140
138
|
_agent_logs(rest[0])
|
|
141
139
|
return
|
|
@@ -183,80 +181,6 @@ def _agent_chat(name: str, message: str) -> None:
|
|
|
183
181
|
sys.exit(1)
|
|
184
182
|
|
|
185
183
|
|
|
186
|
-
def _register_agent(manifest) -> None:
|
|
187
|
-
"""Register a validated manifest with the backend — shared by `new` and `add`."""
|
|
188
|
-
from sdk.client import BackendClient
|
|
189
|
-
|
|
190
|
-
resp = asyncio.run(
|
|
191
|
-
BackendClient.from_config().register_custom_agent(manifest.model_dump(mode="json"))
|
|
192
|
-
)
|
|
193
|
-
resp = resp or {}
|
|
194
|
-
agent_id = resp.get("id") or resp.get("agent_id")
|
|
195
|
-
status = resp.get("status")
|
|
196
|
-
print(f"registered '{manifest.name}' with backend — id={agent_id} status={status}")
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
def _agent_new() -> None:
|
|
200
|
-
from sdk.agents.manifest import AGENTS_DIR, AgentManifest, TriggerSpec
|
|
201
|
-
|
|
202
|
-
print("AgentOS — New Agent")
|
|
203
|
-
print("=" * 40)
|
|
204
|
-
name = input("Name: ").strip()
|
|
205
|
-
description = input("Description: ").strip()
|
|
206
|
-
engine = input("Engine [claude_code]: ").strip() or "claude_code"
|
|
207
|
-
trigger_type = input("Trigger type (schedule/ticket_label/manual/webhook): ").strip()
|
|
208
|
-
|
|
209
|
-
trigger_kwargs: dict = {"type": trigger_type}
|
|
210
|
-
if trigger_type == "schedule":
|
|
211
|
-
trigger_kwargs["cron"] = input("Cron string: ").strip()
|
|
212
|
-
elif trigger_type == "ticket_label":
|
|
213
|
-
trigger_kwargs["ticket_label"] = input("Ticket label: ").strip()
|
|
214
|
-
# manual / webhook need no extra prompt
|
|
215
|
-
|
|
216
|
-
prompt_text = input("Prompt: ").strip()
|
|
217
|
-
|
|
218
|
-
try:
|
|
219
|
-
trigger = TriggerSpec(**trigger_kwargs)
|
|
220
|
-
manifest = AgentManifest(
|
|
221
|
-
name=name,
|
|
222
|
-
description=description,
|
|
223
|
-
engine=engine,
|
|
224
|
-
trigger=trigger,
|
|
225
|
-
prompt=prompt_text,
|
|
226
|
-
)
|
|
227
|
-
except Exception as exc: # noqa: BLE001 — surface pydantic's validation message plainly
|
|
228
|
-
print(f"error: invalid agent — {exc}")
|
|
229
|
-
sys.exit(1)
|
|
230
|
-
|
|
231
|
-
AGENTS_DIR.mkdir(parents=True, exist_ok=True)
|
|
232
|
-
dest = AGENTS_DIR / f"{manifest.name}.yaml"
|
|
233
|
-
dest.write_text(yaml.safe_dump(manifest.model_dump(mode="json"), sort_keys=False))
|
|
234
|
-
print(f"saved manifest to {dest}")
|
|
235
|
-
|
|
236
|
-
_register_agent(manifest)
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
def _agent_add(path: str) -> None:
|
|
240
|
-
from sdk.agents.manifest import AGENTS_DIR, AgentManifest
|
|
241
|
-
|
|
242
|
-
src = Path(path).expanduser()
|
|
243
|
-
try:
|
|
244
|
-
raw_text = src.read_text()
|
|
245
|
-
data = yaml.safe_load(raw_text) or {}
|
|
246
|
-
manifest = AgentManifest(**data)
|
|
247
|
-
except Exception as exc: # noqa: BLE001 — surface a clear validation/IO error
|
|
248
|
-
print(f"error: invalid agent manifest '{path}' — {exc}")
|
|
249
|
-
sys.exit(1)
|
|
250
|
-
|
|
251
|
-
AGENTS_DIR.mkdir(parents=True, exist_ok=True)
|
|
252
|
-
dest = AGENTS_DIR / f"{manifest.name}.yaml"
|
|
253
|
-
if dest.resolve() != src.resolve():
|
|
254
|
-
dest.write_text(raw_text)
|
|
255
|
-
print(f"copied manifest to {dest}")
|
|
256
|
-
|
|
257
|
-
_register_agent(manifest)
|
|
258
|
-
|
|
259
|
-
|
|
260
184
|
def _agent_list() -> None:
|
|
261
185
|
from sdk.agents.manifest import load_agents
|
|
262
186
|
from sdk.client import BackendClient
|
|
@@ -543,201 +467,39 @@ def _default_agents_command(args: list[str]) -> None:
|
|
|
543
467
|
if verb == "list":
|
|
544
468
|
_default_agents_list(_resolve_department_id(dept_id))
|
|
545
469
|
return
|
|
546
|
-
|
|
547
|
-
|
|
548
|
-
|
|
549
|
-
#
|
|
550
|
-
|
|
551
|
-
|
|
552
|
-
|
|
553
|
-
_default_agents_seed(_resolve_department_id(dept_id), tail)
|
|
554
|
-
return
|
|
555
|
-
if verb == "seed-library":
|
|
556
|
-
_require_department_manager(verb)
|
|
557
|
-
_default_agents_seed_library(_resolve_department_id(dept_id), tail)
|
|
558
|
-
return
|
|
559
|
-
if verb in ("enable", "disable") and tail:
|
|
560
|
-
_require_department_manager(verb)
|
|
561
|
-
_default_agents_set_status(
|
|
562
|
-
_resolve_department_id(dept_id), tail[0],
|
|
563
|
-
"active" if verb == "enable" else "disabled",
|
|
564
|
-
)
|
|
565
|
-
return
|
|
566
|
-
if verb == "rm" and tail:
|
|
567
|
-
_require_department_manager(verb)
|
|
568
|
-
_default_agents_rm(_resolve_department_id(dept_id), tail[0])
|
|
470
|
+
|
|
471
|
+
# Seeding / activating / managing department defaults is not owned by the SDK
|
|
472
|
+
# anymore — agents are authored in the web app and written to the backend
|
|
473
|
+
# `agents` table. The CLI is consume-only.
|
|
474
|
+
if verb in ("seed", "seed-library", "activate", "enable", "disable", "rm"):
|
|
475
|
+
print(f"`agentos default-agents {verb}` has been removed — department default "
|
|
476
|
+
"agents are managed in the web app now. Use `agentos default-agents list`.")
|
|
569
477
|
return
|
|
570
478
|
|
|
571
|
-
print(
|
|
572
|
-
"usage: agentos default-agents "
|
|
573
|
-
"<list|activate NAME|seed PATH|seed-library [SLUG]|enable NAME|disable NAME|rm NAME> "
|
|
574
|
-
"[--department-id UUID]"
|
|
575
|
-
)
|
|
576
|
-
print(" (seed/enable/disable/rm require department-manager rights)")
|
|
479
|
+
print("usage: agentos default-agents list [--department-id UUID]")
|
|
577
480
|
sys.exit(1)
|
|
578
481
|
|
|
579
482
|
|
|
580
|
-
def _default_agents_seed(department_id: str, tail: list[str]) -> None:
|
|
581
|
-
"""Create/update ONE department default from a manifest YAML (manager only).
|
|
582
|
-
|
|
583
|
-
Flags: --min-role-tier TIER, --sort-order N, --system.
|
|
584
|
-
"""
|
|
585
|
-
import yaml
|
|
586
|
-
|
|
587
|
-
path = tail[0]
|
|
588
|
-
min_tier, sort_order, is_system = "employee", 0, False
|
|
589
|
-
i = 1
|
|
590
|
-
while i < len(tail):
|
|
591
|
-
if tail[i] == "--min-role-tier" and i + 1 < len(tail):
|
|
592
|
-
min_tier = tail[i + 1]; i += 2; continue
|
|
593
|
-
if tail[i] == "--sort-order" and i + 1 < len(tail):
|
|
594
|
-
sort_order = int(tail[i + 1]); i += 2; continue
|
|
595
|
-
if tail[i] == "--system":
|
|
596
|
-
is_system = True; i += 1; continue
|
|
597
|
-
i += 1
|
|
598
|
-
|
|
599
|
-
from sdk.agents.manifest import AgentManifest
|
|
600
|
-
from sdk.client import BackendClient
|
|
601
|
-
|
|
602
|
-
try:
|
|
603
|
-
raw = yaml.safe_load(Path(path).expanduser().read_text()) or {}
|
|
604
|
-
AgentManifest(**raw) # validate before sending
|
|
605
|
-
except Exception as exc: # noqa: BLE001
|
|
606
|
-
print(f"error: invalid manifest '{path}' — {exc}")
|
|
607
|
-
sys.exit(1)
|
|
608
|
-
|
|
609
|
-
payload = {
|
|
610
|
-
"manifest": raw,
|
|
611
|
-
"min_role_tier": min_tier,
|
|
612
|
-
"is_system_default": is_system,
|
|
613
|
-
"sort_order": sort_order,
|
|
614
|
-
}
|
|
615
|
-
try:
|
|
616
|
-
result = asyncio.run(
|
|
617
|
-
BackendClient.from_config().seed_department_default_agent(department_id, payload)
|
|
618
|
-
)
|
|
619
|
-
except Exception as exc: # noqa: BLE001 — surface 403 (not a manager) clearly
|
|
620
|
-
print(f"error: {exc}")
|
|
621
|
-
sys.exit(1)
|
|
622
|
-
print(f"seeded default agent '{raw.get('name')}' into department {department_id} "
|
|
623
|
-
f"(id={result.get('agent_id')})")
|
|
624
|
-
|
|
625
|
-
|
|
626
|
-
def _default_agents_seed_library(department_id: str, tail: list[str]) -> None:
|
|
627
|
-
"""Seed a whole department's bundled library (manager only). SLUG defaults to 'developers'."""
|
|
628
|
-
from sdk.agents.library import load_department
|
|
629
|
-
from sdk.client import BackendClient
|
|
630
|
-
|
|
631
|
-
slug = tail[0] if tail else "developers"
|
|
632
|
-
try:
|
|
633
|
-
templates = load_department(slug)
|
|
634
|
-
except KeyError:
|
|
635
|
-
print(f"error: no library department '{slug}'")
|
|
636
|
-
sys.exit(1)
|
|
637
|
-
|
|
638
|
-
client = BackendClient.from_config()
|
|
639
|
-
ok = 0
|
|
640
|
-
for t in templates:
|
|
641
|
-
payload = {
|
|
642
|
-
"manifest": t.manifest.model_dump(mode="json"),
|
|
643
|
-
"min_role_tier": t.min_role_tier,
|
|
644
|
-
"is_system_default": t.is_system_default,
|
|
645
|
-
"sort_order": t.sort_order,
|
|
646
|
-
}
|
|
647
|
-
try:
|
|
648
|
-
asyncio.run(client.seed_department_default_agent(department_id, payload))
|
|
649
|
-
print(f" seeded {t.manifest.name}")
|
|
650
|
-
ok += 1
|
|
651
|
-
except Exception as exc: # noqa: BLE001
|
|
652
|
-
print(f" FAILED {t.manifest.name}: {exc}", file=sys.stderr)
|
|
653
|
-
print(f"seeded {ok}/{len(templates)} '{slug}' agents into department {department_id}")
|
|
654
|
-
|
|
655
|
-
|
|
656
|
-
def _default_agents_set_status(department_id: str, name: str, status: str) -> None:
|
|
657
|
-
from sdk.client import BackendClient
|
|
658
|
-
try:
|
|
659
|
-
asyncio.run(
|
|
660
|
-
BackendClient.from_config().update_department_default_agent(
|
|
661
|
-
department_id, name, status
|
|
662
|
-
)
|
|
663
|
-
)
|
|
664
|
-
except Exception as exc: # noqa: BLE001
|
|
665
|
-
print(f"error: {exc}")
|
|
666
|
-
sys.exit(1)
|
|
667
|
-
print(f"default agent '{name}' set to {status}")
|
|
668
|
-
|
|
669
|
-
|
|
670
|
-
def _default_agents_rm(department_id: str, name: str) -> None:
|
|
671
|
-
from sdk.client import BackendClient
|
|
672
|
-
try:
|
|
673
|
-
asyncio.run(
|
|
674
|
-
BackendClient.from_config().delete_department_default_agent(department_id, name)
|
|
675
|
-
)
|
|
676
|
-
except Exception as exc: # noqa: BLE001
|
|
677
|
-
print(f"error: {exc}")
|
|
678
|
-
sys.exit(1)
|
|
679
|
-
print(f"deleted default agent '{name}' from department {department_id}")
|
|
680
|
-
|
|
681
|
-
|
|
682
483
|
def _default_agents_list(department_id: str) -> None:
|
|
484
|
+
"""List the caller's department default agents (read-only, new prompt schema)."""
|
|
683
485
|
from sdk.client import BackendClient
|
|
684
486
|
from sdk.exceptions import BackendFeatureMissing
|
|
685
487
|
|
|
686
488
|
try:
|
|
687
|
-
agents = asyncio.run(
|
|
688
|
-
BackendClient.from_config().list_department_default_agents(department_id)
|
|
689
|
-
)
|
|
489
|
+
agents = asyncio.run(BackendClient.from_config().list_department_agents())
|
|
690
490
|
except BackendFeatureMissing as exc:
|
|
691
|
-
# A 404 here means the endpoint isn't deployed — NOT that the department
|
|
692
|
-
# has no defaults. Say so, so this isn't mistaken for an empty list.
|
|
693
491
|
print(f"error: {exc}")
|
|
694
492
|
sys.exit(1)
|
|
695
493
|
if not agents:
|
|
696
|
-
print(f"no default agents
|
|
697
|
-
f"(a manager can add them with `agentos default-agents seed …`).")
|
|
494
|
+
print(f"no department default agents visible for department {department_id}.")
|
|
698
495
|
return
|
|
699
496
|
|
|
700
|
-
print(f"{'NAME':<
|
|
497
|
+
print(f"{'NAME':<28}{'STATUS':<10}{'SKILLS':<8}SUMMARY")
|
|
701
498
|
for a in agents:
|
|
702
|
-
|
|
703
|
-
|
|
704
|
-
|
|
705
|
-
|
|
706
|
-
|
|
707
|
-
|
|
708
|
-
def _default_agents_activate(department_id: str, name: str) -> None:
|
|
709
|
-
"""Clone a department default into the caller's custom agents + install locally."""
|
|
710
|
-
import yaml as _yaml
|
|
711
|
-
|
|
712
|
-
from sdk.agents.library import find_template
|
|
713
|
-
from sdk.agents.manifest import AGENTS_DIR, SKILLS_DIR
|
|
714
|
-
from sdk.client import BackendClient
|
|
715
|
-
|
|
716
|
-
result = asyncio.run(
|
|
717
|
-
BackendClient.from_config().activate_department_default_agent(department_id, name)
|
|
718
|
-
)
|
|
719
|
-
manifest = result.get("manifest") or {}
|
|
720
|
-
|
|
721
|
-
# Install the manifest locally so agentd / `agent run` can execute it.
|
|
722
|
-
if manifest:
|
|
723
|
-
AGENTS_DIR.mkdir(parents=True, exist_ok=True)
|
|
724
|
-
(AGENTS_DIR / f"{name}.yaml").write_text(
|
|
725
|
-
_yaml.safe_dump(manifest, sort_keys=False)
|
|
726
|
-
)
|
|
727
|
-
|
|
728
|
-
# Copy any skills the template bundles into ~/.agentos/skills.
|
|
729
|
-
template = find_template(name)
|
|
730
|
-
copied_skills: list[str] = []
|
|
731
|
-
if template and template.skills:
|
|
732
|
-
SKILLS_DIR.mkdir(parents=True, exist_ok=True)
|
|
733
|
-
for skill_name, path in template.skills.items():
|
|
734
|
-
(SKILLS_DIR / f"{skill_name}.md").write_text(path.read_text(encoding="utf-8"))
|
|
735
|
-
copied_skills.append(skill_name)
|
|
736
|
-
|
|
737
|
-
print(f"activated '{name}' — cloned to your custom agents (id={result.get('custom_agent_id')})")
|
|
738
|
-
print(f" installed manifest to {AGENTS_DIR / (name + '.yaml')}")
|
|
739
|
-
if copied_skills:
|
|
740
|
-
print(f" installed skills: {', '.join(copied_skills)}")
|
|
499
|
+
prompt = a.get("prompt") or {}
|
|
500
|
+
summary = (prompt.get("system_prompt") if isinstance(prompt, dict) else "") or ""
|
|
501
|
+
print(f"{a.get('name',''):<28}{a.get('status',''):<10}"
|
|
502
|
+
f"{len(a.get('skills') or []):<8}{summary.strip()[:60]}")
|
|
741
503
|
|
|
742
504
|
|
|
743
505
|
# ── agentd ───────────────────────────────────────────────────────────────
|
|
@@ -1202,47 +1202,10 @@ class BackendClient:
|
|
|
1202
1202
|
_check_status(resp.status_code, self._base_url)
|
|
1203
1203
|
return resp.json()
|
|
1204
1204
|
|
|
1205
|
-
# ── Custom agents
|
|
1206
|
-
|
|
1207
|
-
|
|
1208
|
-
|
|
1209
|
-
|
|
1210
|
-
Guards the network boundary: a manifest carrying an inline secret is
|
|
1211
|
-
rejected locally before anything is sent — custom-agent secrets stay on
|
|
1212
|
-
the machine where the agent is installed, never in the backend DB.
|
|
1213
|
-
"""
|
|
1214
|
-
from sdk.agents.manifest import assert_secret_free
|
|
1215
|
-
assert_secret_free(manifest, "manifest")
|
|
1216
|
-
async with httpx.AsyncClient(timeout=15.0) as client:
|
|
1217
|
-
resp = await client.post(
|
|
1218
|
-
self._url("/api/v1/agents/register"),
|
|
1219
|
-
json=manifest,
|
|
1220
|
-
headers=self._headers,
|
|
1221
|
-
)
|
|
1222
|
-
_check_status(resp.status_code, self._base_url)
|
|
1223
|
-
return resp.json()
|
|
1224
|
-
|
|
1225
|
-
async def register_department_agent(self, manifest: dict) -> dict:
|
|
1226
|
-
"""POST /api/v1/department-agents/register — publish a DEPARTMENT default.
|
|
1227
|
-
|
|
1228
|
-
Head-gated server-side (team_lead+ of the caller's own department, or
|
|
1229
|
-
master → 403 otherwise). Same secret-free guard as a custom agent: the
|
|
1230
|
-
manifest may only carry ${ENV_VAR} placeholders, never raw secrets —
|
|
1231
|
-
department agents resolve their keys from the backend/host environment.
|
|
1232
|
-
"""
|
|
1233
|
-
from sdk.agents.manifest import assert_secret_free
|
|
1234
|
-
assert_secret_free(manifest, "manifest")
|
|
1235
|
-
url = self._url("/api/v1/department-agents/register")
|
|
1236
|
-
async with httpx.AsyncClient(timeout=15.0) as client:
|
|
1237
|
-
resp = await client.post(url, json=manifest, headers=self._headers)
|
|
1238
|
-
if resp.status_code == 404:
|
|
1239
|
-
raise BackendFeatureMissing("Department agent registration", url)
|
|
1240
|
-
if resp.status_code == 403:
|
|
1241
|
-
raise PermissionError(
|
|
1242
|
-
"only a department manager (team_lead+) may publish a department default agent"
|
|
1243
|
-
)
|
|
1244
|
-
_check_status(resp.status_code, self._base_url)
|
|
1245
|
-
return resp.json()
|
|
1205
|
+
# ── Custom agents (CONSUME-ONLY) ──────────────────────────────────
|
|
1206
|
+
# Agent CREATION is not owned by the SDK anymore — agents are authored in the
|
|
1207
|
+
# web app and written to the backend `agents` table (prompt/secrets/skills).
|
|
1208
|
+
# This client only lists/reads them and manages per-agent status/delete.
|
|
1246
1209
|
|
|
1247
1210
|
async def list_custom_agents(self) -> list[dict]:
|
|
1248
1211
|
"""GET /api/v1/agents — list registered custom agents."""
|
|
@@ -1298,66 +1261,10 @@ class BackendClient:
|
|
|
1298
1261
|
_check_status(resp.status_code, self._base_url)
|
|
1299
1262
|
return resp.json().get("agents", [])
|
|
1300
1263
|
|
|
1301
|
-
#
|
|
1302
|
-
|
|
1303
|
-
|
|
1304
|
-
|
|
1305
|
-
url = self._url(f"/api/v1/departments/{department_id}/default-agents")
|
|
1306
|
-
async with httpx.AsyncClient(timeout=15.0) as client:
|
|
1307
|
-
resp = await client.get(url, headers=self._headers)
|
|
1308
|
-
if resp.status_code == 404:
|
|
1309
|
-
raise BackendFeatureMissing("Department default agents", url)
|
|
1310
|
-
_check_status(resp.status_code, self._base_url)
|
|
1311
|
-
return resp.json().get("agents", [])
|
|
1312
|
-
|
|
1313
|
-
async def seed_department_default_agent(self, department_id: str, payload: dict) -> dict:
|
|
1314
|
-
"""POST /api/v1/departments/{id}/default-agents/seed — upsert one template.
|
|
1315
|
-
|
|
1316
|
-
Same secret-free guard as custom-agent registration: default-agent
|
|
1317
|
-
templates are stored sans secrets; ${ENV_VAR} placeholders only.
|
|
1318
|
-
"""
|
|
1319
|
-
from sdk.agents.manifest import assert_secret_free
|
|
1320
|
-
assert_secret_free(payload, "default_agent")
|
|
1321
|
-
async with httpx.AsyncClient(timeout=15.0) as client:
|
|
1322
|
-
resp = await client.post(
|
|
1323
|
-
self._url(f"/api/v1/departments/{department_id}/default-agents/seed"),
|
|
1324
|
-
json=payload,
|
|
1325
|
-
headers=self._headers,
|
|
1326
|
-
)
|
|
1327
|
-
_check_status(resp.status_code, self._base_url)
|
|
1328
|
-
return resp.json()
|
|
1329
|
-
|
|
1330
|
-
async def activate_department_default_agent(self, department_id: str, name: str) -> dict:
|
|
1331
|
-
"""POST /api/v1/departments/{id}/default-agents/{name}/activate — clone to my custom agents."""
|
|
1332
|
-
async with httpx.AsyncClient(timeout=15.0) as client:
|
|
1333
|
-
resp = await client.post(
|
|
1334
|
-
self._url(f"/api/v1/departments/{department_id}/default-agents/{name}/activate"),
|
|
1335
|
-
headers=self._headers,
|
|
1336
|
-
)
|
|
1337
|
-
_check_status(resp.status_code, self._base_url)
|
|
1338
|
-
return resp.json()
|
|
1339
|
-
|
|
1340
|
-
async def update_department_default_agent(
|
|
1341
|
-
self, department_id: str, name: str, status: str
|
|
1342
|
-
) -> dict:
|
|
1343
|
-
"""PATCH /api/v1/departments/{id}/default-agents/{name} — enable/disable (manager only)."""
|
|
1344
|
-
async with httpx.AsyncClient(timeout=15.0) as client:
|
|
1345
|
-
resp = await client.patch(
|
|
1346
|
-
self._url(f"/api/v1/departments/{department_id}/default-agents/{name}"),
|
|
1347
|
-
json={"status": status},
|
|
1348
|
-
headers=self._headers,
|
|
1349
|
-
)
|
|
1350
|
-
_check_status(resp.status_code, self._base_url)
|
|
1351
|
-
return resp.json()
|
|
1352
|
-
|
|
1353
|
-
async def delete_department_default_agent(self, department_id: str, name: str) -> None:
|
|
1354
|
-
"""DELETE /api/v1/departments/{id}/default-agents/{name} — soft-delete (manager only)."""
|
|
1355
|
-
async with httpx.AsyncClient(timeout=15.0) as client:
|
|
1356
|
-
resp = await client.delete(
|
|
1357
|
-
self._url(f"/api/v1/departments/{department_id}/default-agents/{name}"),
|
|
1358
|
-
headers=self._headers,
|
|
1359
|
-
)
|
|
1360
|
-
_check_status(resp.status_code, self._base_url)
|
|
1264
|
+
# NOTE: the legacy per-department "default agents" endpoints
|
|
1265
|
+
# (/departments/{id}/default-agents[/seed|/activate|...]) were retired — that
|
|
1266
|
+
# table never existed. Department defaults are now plain rows in the `agents`
|
|
1267
|
+
# table (relation_type=department), read via `list_department_agents` above.
|
|
1361
1268
|
|
|
1362
1269
|
async def list_connected_services(self) -> list[str]:
|
|
1363
1270
|
"""Best-effort: services the caller has connected (for connector discovery).
|
|
@@ -3957,15 +3957,15 @@ async def handle_orchestrate_auto_local_run(
|
|
|
3957
3957
|
async def handle_agent_create(
|
|
3958
3958
|
client: BackendClient, payload: dict, send: Callable
|
|
3959
3959
|
) -> None:
|
|
3960
|
-
"""agent:create —
|
|
3960
|
+
"""agent:create — REMOVED. Agent creation is not owned by the SDK anymore;
|
|
3961
|
+
agents are authored in the web app and written to the backend `agents` table.
|
|
3962
|
+
"""
|
|
3961
3963
|
thread_id = str(payload.get("thread_id", "agent"))
|
|
3962
|
-
|
|
3963
|
-
|
|
3964
|
-
|
|
3965
|
-
|
|
3966
|
-
|
|
3967
|
-
except Exception as exc:
|
|
3968
|
-
await send(_error_envelope(thread_id, exc).model_dump())
|
|
3964
|
+
await send(_error_envelope(
|
|
3965
|
+
thread_id,
|
|
3966
|
+
RuntimeError("Agent creation has moved to the web app — the desktop SDK is "
|
|
3967
|
+
"consume-only (list/run/manage). This action is no longer available."),
|
|
3968
|
+
).model_dump())
|
|
3969
3969
|
|
|
3970
3970
|
|
|
3971
3971
|
async def handle_agent_list(
|