devops-bot-sdk 1.4.117__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.117 → devops_bot_sdk-1.4.119}/PKG-INFO +1 -1
- {devops_bot_sdk-1.4.117 → devops_bot_sdk-1.4.119}/devops_bot_sdk.egg-info/PKG-INFO +1 -1
- {devops_bot_sdk-1.4.117 → devops_bot_sdk-1.4.119}/devops_bot_sdk.egg-info/SOURCES.txt +1 -1
- {devops_bot_sdk-1.4.117 → devops_bot_sdk-1.4.119}/pyproject.toml +4 -1
- {devops_bot_sdk-1.4.117 → devops_bot_sdk-1.4.119}/sdk/__init__.py +3 -3
- {devops_bot_sdk-1.4.117 → devops_bot_sdk-1.4.119}/sdk/agentd.py +6 -7
- {devops_bot_sdk-1.4.117 → devops_bot_sdk-1.4.119}/sdk/agents/prompt_studio.py +104 -0
- {devops_bot_sdk-1.4.117 → devops_bot_sdk-1.4.119}/sdk/cli.py +25 -263
- {devops_bot_sdk-1.4.117 → devops_bot_sdk-1.4.119}/sdk/client.py +81 -91
- {devops_bot_sdk-1.4.117 → devops_bot_sdk-1.4.119}/sdk/config.py +68 -0
- {devops_bot_sdk-1.4.117 → devops_bot_sdk-1.4.119}/sdk/ipc/handlers.py +8 -8
- {devops_bot_sdk-1.4.117 → devops_bot_sdk-1.4.119}/sdk/ui/api.py +141 -222
- devops_bot_sdk-1.4.119/sdk/ui/ip_allowlist.py +110 -0
- {devops_bot_sdk-1.4.117 → devops_bot_sdk-1.4.119}/sdk/ui/security.py +9 -2
- {devops_bot_sdk-1.4.117 → devops_bot_sdk-1.4.119}/sdk/ui/server.py +16 -3
- {devops_bot_sdk-1.4.117 → devops_bot_sdk-1.4.119}/sdk/ui/static/app.js +17 -35
- {devops_bot_sdk-1.4.117 → devops_bot_sdk-1.4.119}/sdk/ui/static/custom-agents.html +7 -8
- {devops_bot_sdk-1.4.117 → devops_bot_sdk-1.4.119}/sdk/ui/static/default-agents.html +3 -3
- devops_bot_sdk-1.4.117/sdk/ui/static/prompt-studio.html +0 -92
- {devops_bot_sdk-1.4.117 → devops_bot_sdk-1.4.119}/README.md +0 -0
- {devops_bot_sdk-1.4.117 → devops_bot_sdk-1.4.119}/devops_bot_sdk.egg-info/dependency_links.txt +0 -0
- {devops_bot_sdk-1.4.117 → devops_bot_sdk-1.4.119}/devops_bot_sdk.egg-info/entry_points.txt +0 -0
- {devops_bot_sdk-1.4.117 → devops_bot_sdk-1.4.119}/devops_bot_sdk.egg-info/requires.txt +0 -0
- {devops_bot_sdk-1.4.117 → devops_bot_sdk-1.4.119}/devops_bot_sdk.egg-info/top_level.txt +0 -0
- {devops_bot_sdk-1.4.117 → devops_bot_sdk-1.4.119}/sdk/agents/__init__.py +0 -0
- {devops_bot_sdk-1.4.117 → devops_bot_sdk-1.4.119}/sdk/agents/builder.py +0 -0
- {devops_bot_sdk-1.4.117 → devops_bot_sdk-1.4.119}/sdk/agents/chat.py +0 -0
- {devops_bot_sdk-1.4.117 → devops_bot_sdk-1.4.119}/sdk/agents/connectors.py +0 -0
- {devops_bot_sdk-1.4.117 → devops_bot_sdk-1.4.119}/sdk/agents/engines/__init__.py +0 -0
- {devops_bot_sdk-1.4.117 → devops_bot_sdk-1.4.119}/sdk/agents/engines/claude_code.py +0 -0
- {devops_bot_sdk-1.4.117 → devops_bot_sdk-1.4.119}/sdk/agents/engines/http.py +0 -0
- {devops_bot_sdk-1.4.117 → devops_bot_sdk-1.4.119}/sdk/agents/engines/mcp.py +0 -0
- {devops_bot_sdk-1.4.117 → devops_bot_sdk-1.4.119}/sdk/agents/engines/n8n.py +0 -0
- {devops_bot_sdk-1.4.117 → devops_bot_sdk-1.4.119}/sdk/agents/engines/shell.py +0 -0
- {devops_bot_sdk-1.4.117 → devops_bot_sdk-1.4.119}/sdk/agents/integrations.py +0 -0
- {devops_bot_sdk-1.4.117 → devops_bot_sdk-1.4.119}/sdk/agents/inventory.py +0 -0
- {devops_bot_sdk-1.4.117 → devops_bot_sdk-1.4.119}/sdk/agents/library/__init__.py +0 -0
- {devops_bot_sdk-1.4.117 → devops_bot_sdk-1.4.119}/sdk/agents/manifest.py +0 -0
- {devops_bot_sdk-1.4.117 → devops_bot_sdk-1.4.119}/sdk/agents/prompt_template.py +0 -0
- {devops_bot_sdk-1.4.117 → devops_bot_sdk-1.4.119}/sdk/agents/prompt_vars.py +0 -0
- {devops_bot_sdk-1.4.117 → devops_bot_sdk-1.4.119}/sdk/agents/run_log.py +0 -0
- {devops_bot_sdk-1.4.117 → devops_bot_sdk-1.4.119}/sdk/agents/secrets.py +0 -0
- {devops_bot_sdk-1.4.117 → devops_bot_sdk-1.4.119}/sdk/agents/usage.py +0 -0
- {devops_bot_sdk-1.4.117 → devops_bot_sdk-1.4.119}/sdk/agents/varstore.py +0 -0
- {devops_bot_sdk-1.4.117 → devops_bot_sdk-1.4.119}/sdk/collectors/__init__.py +0 -0
- {devops_bot_sdk-1.4.117 → devops_bot_sdk-1.4.119}/sdk/collectors/files.py +0 -0
- {devops_bot_sdk-1.4.117 → devops_bot_sdk-1.4.119}/sdk/collectors/process.py +0 -0
- {devops_bot_sdk-1.4.117 → devops_bot_sdk-1.4.119}/sdk/collectors/screenshot.py +0 -0
- {devops_bot_sdk-1.4.117 → devops_bot_sdk-1.4.119}/sdk/crucial.py +0 -0
- {devops_bot_sdk-1.4.117 → devops_bot_sdk-1.4.119}/sdk/design_verify/__init__.py +0 -0
- {devops_bot_sdk-1.4.117 → devops_bot_sdk-1.4.119}/sdk/design_verify/assets.py +0 -0
- {devops_bot_sdk-1.4.117 → devops_bot_sdk-1.4.119}/sdk/design_verify/bootstrap.py +0 -0
- {devops_bot_sdk-1.4.117 → devops_bot_sdk-1.4.119}/sdk/design_verify/browser.py +0 -0
- {devops_bot_sdk-1.4.117 → devops_bot_sdk-1.4.119}/sdk/design_verify/compare.py +0 -0
- {devops_bot_sdk-1.4.117 → devops_bot_sdk-1.4.119}/sdk/design_verify/loop.py +0 -0
- {devops_bot_sdk-1.4.117 → devops_bot_sdk-1.4.119}/sdk/exceptions.py +0 -0
- {devops_bot_sdk-1.4.117 → devops_bot_sdk-1.4.119}/sdk/git_ops.py +0 -0
- {devops_bot_sdk-1.4.117 → devops_bot_sdk-1.4.119}/sdk/graphify.py +0 -0
- {devops_bot_sdk-1.4.117 → devops_bot_sdk-1.4.119}/sdk/hooks/__init__.py +0 -0
- {devops_bot_sdk-1.4.117 → devops_bot_sdk-1.4.119}/sdk/hooks/crucial_guard.py +0 -0
- {devops_bot_sdk-1.4.117 → devops_bot_sdk-1.4.119}/sdk/ipc/__init__.py +0 -0
- {devops_bot_sdk-1.4.117 → devops_bot_sdk-1.4.119}/sdk/ipc/electron_bridge.py +0 -0
- {devops_bot_sdk-1.4.117 → devops_bot_sdk-1.4.119}/sdk/local_exec.py +0 -0
- {devops_bot_sdk-1.4.117 → devops_bot_sdk-1.4.119}/sdk/models/__init__.py +0 -0
- {devops_bot_sdk-1.4.117 → devops_bot_sdk-1.4.119}/sdk/models/envelope.py +0 -0
- {devops_bot_sdk-1.4.117 → devops_bot_sdk-1.4.119}/sdk/models/requests.py +0 -0
- {devops_bot_sdk-1.4.117 → devops_bot_sdk-1.4.119}/sdk/models/responses.py +0 -0
- {devops_bot_sdk-1.4.117 → devops_bot_sdk-1.4.119}/sdk/models/snapshots.py +0 -0
- {devops_bot_sdk-1.4.117 → devops_bot_sdk-1.4.119}/sdk/platform_compat.py +0 -0
- {devops_bot_sdk-1.4.117 → devops_bot_sdk-1.4.119}/sdk/py.typed +0 -0
- {devops_bot_sdk-1.4.117 → devops_bot_sdk-1.4.119}/sdk/run_auto.py +0 -0
- {devops_bot_sdk-1.4.117 → devops_bot_sdk-1.4.119}/sdk/sse.py +0 -0
- {devops_bot_sdk-1.4.117 → devops_bot_sdk-1.4.119}/sdk/test.py +0 -0
- {devops_bot_sdk-1.4.117 → devops_bot_sdk-1.4.119}/sdk/test_pipeline.py +0 -0
- {devops_bot_sdk-1.4.117 → devops_bot_sdk-1.4.119}/sdk/ui/__init__.py +0 -0
- {devops_bot_sdk-1.4.117 → devops_bot_sdk-1.4.119}/sdk/ui/dashboard.py +0 -0
- {devops_bot_sdk-1.4.117 → devops_bot_sdk-1.4.119}/sdk/ui/jobs.py +0 -0
- {devops_bot_sdk-1.4.117 → devops_bot_sdk-1.4.119}/sdk/ui/runner.py +0 -0
- {devops_bot_sdk-1.4.117 → devops_bot_sdk-1.4.119}/sdk/ui/service.py +0 -0
- {devops_bot_sdk-1.4.117 → devops_bot_sdk-1.4.119}/sdk/ui/setup_manager.py +0 -0
- {devops_bot_sdk-1.4.117 → devops_bot_sdk-1.4.119}/sdk/ui/static/agents.html +0 -0
- {devops_bot_sdk-1.4.117 → devops_bot_sdk-1.4.119}/sdk/ui/static/index.html +0 -0
- {devops_bot_sdk-1.4.117 → devops_bot_sdk-1.4.119}/sdk/ui/static/runs.html +0 -0
- {devops_bot_sdk-1.4.117 → devops_bot_sdk-1.4.119}/sdk/ui/static/setup.html +0 -0
- {devops_bot_sdk-1.4.117 → devops_bot_sdk-1.4.119}/sdk/ui/static/styles.css +0 -0
- {devops_bot_sdk-1.4.117 → devops_bot_sdk-1.4.119}/sdk/ui/static/tickets.html +0 -0
- {devops_bot_sdk-1.4.117 → devops_bot_sdk-1.4.119}/sdk/updater.py +0 -0
- {devops_bot_sdk-1.4.117 → devops_bot_sdk-1.4.119}/setup.cfg +0 -0
- {devops_bot_sdk-1.4.117 → devops_bot_sdk-1.4.119}/tests/test_agent_chat_features.py +0 -0
- {devops_bot_sdk-1.4.117 → devops_bot_sdk-1.4.119}/tests/test_agents_connectors_builder.py +0 -0
|
@@ -67,6 +67,7 @@ sdk/models/snapshots.py
|
|
|
67
67
|
sdk/ui/__init__.py
|
|
68
68
|
sdk/ui/api.py
|
|
69
69
|
sdk/ui/dashboard.py
|
|
70
|
+
sdk/ui/ip_allowlist.py
|
|
70
71
|
sdk/ui/jobs.py
|
|
71
72
|
sdk/ui/runner.py
|
|
72
73
|
sdk/ui/security.py
|
|
@@ -78,7 +79,6 @@ sdk/ui/static/app.js
|
|
|
78
79
|
sdk/ui/static/custom-agents.html
|
|
79
80
|
sdk/ui/static/default-agents.html
|
|
80
81
|
sdk/ui/static/index.html
|
|
81
|
-
sdk/ui/static/prompt-studio.html
|
|
82
82
|
sdk/ui/static/runs.html
|
|
83
83
|
sdk/ui/static/setup.html
|
|
84
84
|
sdk/ui/static/styles.css
|
|
@@ -4,7 +4,7 @@ build-backend = "setuptools.build_meta"
|
|
|
4
4
|
|
|
5
5
|
[project]
|
|
6
6
|
name = "devops-bot-sdk"
|
|
7
|
-
|
|
7
|
+
dynamic = ["version"] # single source of truth: sdk/__init__.py __version__
|
|
8
8
|
description = "DevOps Bot Desktop SDK — thin client for the AgentOS Electron desktop app"
|
|
9
9
|
readme = "README.md"
|
|
10
10
|
license = "LicenseRef-Proprietary"
|
|
@@ -49,6 +49,9 @@ agentos-ui = "sdk.ui.server:main" # local web control panel (needs the
|
|
|
49
49
|
Homepage = "https://agentos.io"
|
|
50
50
|
Repository = "https://github.com/consultancy-outfit/DevOpsBot-SDK"
|
|
51
51
|
|
|
52
|
+
[tool.setuptools.dynamic]
|
|
53
|
+
version = { attr = "sdk.__version__" } # bump sdk/__init__.py → wheel + client + server all follow
|
|
54
|
+
|
|
52
55
|
[tool.setuptools.packages.find]
|
|
53
56
|
where = ["."]
|
|
54
57
|
include = ["sdk*"]
|
|
@@ -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,8 +30,8 @@ Rules:
|
|
|
30
30
|
- All data egress through submit_webhook only
|
|
31
31
|
"""
|
|
32
32
|
|
|
33
|
-
__version__ = "1.4.
|
|
34
|
-
__author__ = "AgentOS"
|
|
33
|
+
__version__ = "1.4.119" # SINGLE SOURCE OF TRUTH — bump on every change; pyproject,
|
|
34
|
+
__author__ = "AgentOS" # sdk.client.SDK_VERSION and the UI server version all read this.
|
|
35
35
|
|
|
36
36
|
from sdk.client import BackendClient
|
|
37
37
|
from sdk.exceptions import (
|
|
@@ -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
|
|
@@ -92,6 +92,110 @@ def build_comments_prompt(system_prompt: str) -> str:
|
|
|
92
92
|
return COMMENTS_INSTRUCTION.replace("__SYSTEM_PROMPT__", system_prompt.strip())
|
|
93
93
|
|
|
94
94
|
|
|
95
|
+
# ── Generate: name + type + job → a full system prompt with {{variables}} ────
|
|
96
|
+
# This is the claude_code engine's authoring instruction. It runs LOCALLY in the
|
|
97
|
+
# SDK (run_claude_local); the AI backend calls this SDK endpoint on the VPS.
|
|
98
|
+
|
|
99
|
+
GENERATE_INSTRUCTION = (
|
|
100
|
+
"You are an expert prompt engineer. Write the SYSTEM PROMPT for a reusable AI "
|
|
101
|
+
"agent, given its NAME, TYPE, and main JOB. Produce a single, self-contained "
|
|
102
|
+
"prompt that follows EXACTLY this structure and Markdown headings:\n"
|
|
103
|
+
"# Role\nYou are <NAME>, <one-line identity from the type and job>.\n"
|
|
104
|
+
"## Objective\n<one paragraph: the single outcome this agent must produce>\n"
|
|
105
|
+
"## Rules\n- <hard constraints and guardrails>\n"
|
|
106
|
+
"- Never embed secrets; reference credentials as ${ENV_VAR} placeholders.\n"
|
|
107
|
+
"## Workflow\n1. <ordered steps to do the job>\n"
|
|
108
|
+
"## Output\n<exactly what the agent returns and in what format>\n\n"
|
|
109
|
+
"VARIABLE RULE (critical): any value the user must supply AT RUN TIME must "
|
|
110
|
+
"appear in the prompt text as {{variable_name}} (double curly braces, "
|
|
111
|
+
"snake_case). Only create a variable when the job genuinely needs a per-run "
|
|
112
|
+
"input; a self-contained agent may have NONE. Do not invent placeholders for "
|
|
113
|
+
"things the agent can work out itself.\n"
|
|
114
|
+
"Return ONLY a single minified JSON object, no prose, of the exact shape:\n"
|
|
115
|
+
'{"system_prompt":"<full prompt text with \\n line breaks and {{vars}} inline>",'
|
|
116
|
+
'"variables":[{"name":"","label":"","description":"","type":"string"}]}\n'
|
|
117
|
+
"Every {{variable}} in system_prompt MUST have a matching entry in variables, "
|
|
118
|
+
"and vice-versa. If there are none, return \"variables\": [].\n\n"
|
|
119
|
+
"AGENT NAME: __NAME__\nAGENT TYPE: __TYPE__\nMAIN JOB: __JOB____CONTEXT__"
|
|
120
|
+
)
|
|
121
|
+
|
|
122
|
+
REGENERATE_INSTRUCTION = (
|
|
123
|
+
"You are an expert prompt engineer. REGENERATE and improve the EXISTING system "
|
|
124
|
+
"prompt below. The same rules as a fresh prompt apply: keep the exact structure "
|
|
125
|
+
"(# Role / ## Objective / ## Rules / ## Workflow / ## Output), keep "
|
|
126
|
+
"{{variable_name}} placeholders for every per-run input, and keep every "
|
|
127
|
+
"{{variable}} and the variables list in sync. Preserve the original intent and "
|
|
128
|
+
"any working variables; fix structure, clarity, missing sections, weak rules, "
|
|
129
|
+
"and vague output specs. Apply the FEEDBACK if given.\n"
|
|
130
|
+
"Return ONLY a single minified JSON object of the exact shape:\n"
|
|
131
|
+
'{"system_prompt":"...","variables":[{"name":"","label":"","description":"","type":"string"}]}\n\n'
|
|
132
|
+
"__META__FEEDBACK: __FEEDBACK__\n\nEXISTING SYSTEM PROMPT:\n```\n__PROMPT__\n```"
|
|
133
|
+
)
|
|
134
|
+
|
|
135
|
+
|
|
136
|
+
def build_generate_prompt(agent_name: str, agent_type: str, job: str,
|
|
137
|
+
context: str = "") -> str:
|
|
138
|
+
ctx = f"\nADDITIONAL CONTEXT: {context.strip()}" if context and context.strip() else ""
|
|
139
|
+
return (
|
|
140
|
+
GENERATE_INSTRUCTION
|
|
141
|
+
.replace("__NAME__", (agent_name or "").strip())
|
|
142
|
+
.replace("__TYPE__", (agent_type or "").strip())
|
|
143
|
+
.replace("__JOB__", (job or "").strip())
|
|
144
|
+
.replace("__CONTEXT__", ctx)
|
|
145
|
+
)
|
|
146
|
+
|
|
147
|
+
|
|
148
|
+
def build_regenerate_prompt(system_prompt: str, feedback: str = "",
|
|
149
|
+
agent_name: str = "", agent_type: str = "",
|
|
150
|
+
job: str = "") -> str:
|
|
151
|
+
meta_bits = []
|
|
152
|
+
if (agent_name or "").strip():
|
|
153
|
+
meta_bits.append(f"AGENT NAME: {agent_name.strip()}")
|
|
154
|
+
if (agent_type or "").strip():
|
|
155
|
+
meta_bits.append(f"AGENT TYPE: {agent_type.strip()}")
|
|
156
|
+
if (job or "").strip():
|
|
157
|
+
meta_bits.append(f"MAIN JOB: {job.strip()}")
|
|
158
|
+
meta = ("\n".join(meta_bits) + "\n") if meta_bits else ""
|
|
159
|
+
return (
|
|
160
|
+
REGENERATE_INSTRUCTION
|
|
161
|
+
.replace("__META__", meta)
|
|
162
|
+
.replace("__FEEDBACK__", (feedback or "").strip() or "(none — just improve quality)")
|
|
163
|
+
.replace("__PROMPT__", (system_prompt or "").strip())
|
|
164
|
+
)
|
|
165
|
+
|
|
166
|
+
|
|
167
|
+
def parse_generation(model_output: str) -> dict:
|
|
168
|
+
"""Parse a generate/regenerate model reply into ``{system_prompt, variables}``.
|
|
169
|
+
|
|
170
|
+
Placeholders present in the produced prompt are authoritative for the variable
|
|
171
|
+
LIST (so prompt ↔ schema can't drift); the model's ``variables`` array only
|
|
172
|
+
supplies each one's label/description/type. Tolerant of fences/prose.
|
|
173
|
+
"""
|
|
174
|
+
from sdk.agents.prompt_vars import extract_variables
|
|
175
|
+
|
|
176
|
+
data = _extract_json(model_output)
|
|
177
|
+
system_prompt = str(data.get("system_prompt", "")).strip() if isinstance(data, dict) else ""
|
|
178
|
+
|
|
179
|
+
meta: dict[str, dict] = {}
|
|
180
|
+
for v in (data.get("variables", []) if isinstance(data, dict) else []):
|
|
181
|
+
if isinstance(v, dict) and str(v.get("name", "")).strip():
|
|
182
|
+
meta[str(v["name"]).strip()] = v
|
|
183
|
+
elif isinstance(v, str) and v.strip():
|
|
184
|
+
meta[v.strip()] = {"name": v.strip()}
|
|
185
|
+
|
|
186
|
+
variables = []
|
|
187
|
+
for name in extract_variables(system_prompt):
|
|
188
|
+
m = meta.get(name, {})
|
|
189
|
+
vtype = str(m.get("type", "string")).strip() or "string"
|
|
190
|
+
variables.append({
|
|
191
|
+
"name": name,
|
|
192
|
+
"label": str(m.get("label") or name.replace("_", " ").title()).strip(),
|
|
193
|
+
"description": str(m.get("description", "")).strip(),
|
|
194
|
+
"type": vtype,
|
|
195
|
+
})
|
|
196
|
+
return {"system_prompt": system_prompt, "variables": variables}
|
|
197
|
+
|
|
198
|
+
|
|
95
199
|
def _extract_json(model_output: str) -> dict:
|
|
96
200
|
text = (model_output or "").strip()
|
|
97
201
|
if "```" in text:
|
|
@@ -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 ───────────────────────────────────────────────────────────────
|