handler-ai 0.1.0__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- handler_ai-0.1.0.dist-info/METADATA +60 -0
- handler_ai-0.1.0.dist-info/RECORD +234 -0
- handler_ai-0.1.0.dist-info/WHEEL +4 -0
- handler_ai-0.1.0.dist-info/entry_points.txt +6 -0
- handler_cli/__init__.py +1 -0
- handler_cli/account_commands.py +314 -0
- handler_cli/addon_commands.py +128 -0
- handler_cli/agent_commands.py +139 -0
- handler_cli/app_commands.py +175 -0
- handler_cli/app_window.py +85 -0
- handler_cli/automation_commands.py +309 -0
- handler_cli/bridge.py +42 -0
- handler_cli/cli.py +326 -0
- handler_cli/client.py +81 -0
- handler_cli/daemon_commands.py +108 -0
- handler_cli/image_commands.py +102 -0
- handler_cli/inbox_commands.py +33 -0
- handler_cli/output.py +12 -0
- handler_cli/project_commands.py +63 -0
- handler_cli/provider_commands.py +113 -0
- handler_cli/secret_commands.py +64 -0
- handler_cli/session_bridge.py +93 -0
- handler_cli/session_commands.py +485 -0
- handler_cli/setup.py +120 -0
- handler_cli/terminal.py +280 -0
- handler_cli/terminal_client.py +37 -0
- handler_contracts/__init__.py +642 -0
- handler_contracts/account.py +36 -0
- handler_contracts/acp_endpoint.py +69 -0
- handler_contracts/addon.py +147 -0
- handler_contracts/agent_spec.py +75 -0
- handler_contracts/automation.py +285 -0
- handler_contracts/common.py +229 -0
- handler_contracts/control.py +1208 -0
- handler_contracts/harness.py +300 -0
- handler_contracts/miniapp.py +141 -0
- handler_contracts/provider.py +82 -0
- handler_contracts/remote.py +167 -0
- handler_contracts/sandbox.py +199 -0
- handler_contracts/schemas.py +313 -0
- handler_contracts/terminal.py +32 -0
- handler_control/__init__.py +7 -0
- handler_control/access.py +62 -0
- handler_control/accounts.py +279 -0
- handler_control/addon_install.py +268 -0
- handler_control/addons.py +118 -0
- handler_control/agent_state.py +46 -0
- handler_control/agents.py +257 -0
- handler_control/api/__init__.py +137 -0
- handler_control/api/accounts.py +231 -0
- handler_control/api/acp.py +116 -0
- handler_control/api/addons.py +95 -0
- handler_control/api/agents.py +150 -0
- handler_control/api/apps.py +104 -0
- handler_control/api/automations.py +128 -0
- handler_control/api/bindings.py +105 -0
- handler_control/api/builtin_agents.py +22 -0
- handler_control/api/caches.py +79 -0
- handler_control/api/catalog.py +197 -0
- handler_control/api/common.py +205 -0
- handler_control/api/contexts.py +88 -0
- handler_control/api/folders.py +51 -0
- handler_control/api/images.py +164 -0
- handler_control/api/inbox.py +21 -0
- handler_control/api/projects.py +170 -0
- handler_control/api/providers.py +107 -0
- handler_control/api/remote.py +45 -0
- handler_control/api/secrets.py +177 -0
- handler_control/api/sessions.py +192 -0
- handler_control/api/skills.py +270 -0
- handler_control/app_api.py +84 -0
- handler_control/app_http.py +214 -0
- handler_control/app_remote.py +206 -0
- handler_control/apps.py +867 -0
- handler_control/auth.py +48 -0
- handler_control/automation_config.py +255 -0
- handler_control/automations.py +1134 -0
- handler_control/bindings.py +149 -0
- handler_control/builds.py +341 -0
- handler_control/builtin_agents.py +82 -0
- handler_control/caches.py +113 -0
- handler_control/checks.py +287 -0
- handler_control/compose.py +317 -0
- handler_control/config_files.py +41 -0
- handler_control/contexts.py +75 -0
- handler_control/daemon.py +64 -0
- handler_control/data/automation/bin/handler-llm +85 -0
- handler_control/data/automations/env-issues/automation.toml +17 -0
- handler_control/data/automations/env-issues/extract.py +73 -0
- handler_control/data/automations/env-issues/findings.schema.json +28 -0
- handler_control/data/automations/env-issues/prompt.md +19 -0
- handler_control/data/builtin-agents/skill-creator/agent.toml +10 -0
- handler_control/data/builtin-agents/skill-creator/instructions.md +5 -0
- handler_control/data/builtin-agents/skill-creator/skills/handler-skills/SKILL.md +44 -0
- handler_control/data/builtin-agents/skill-creator/skills/skill-creator/LICENSE.txt +202 -0
- handler_control/data/builtin-agents/skill-creator/skills/skill-creator/SKILL.md +485 -0
- handler_control/data/builtin-agents/skill-creator/skills/skill-creator/agents/analyzer.md +274 -0
- handler_control/data/builtin-agents/skill-creator/skills/skill-creator/agents/comparator.md +202 -0
- handler_control/data/builtin-agents/skill-creator/skills/skill-creator/agents/grader.md +223 -0
- handler_control/data/builtin-agents/skill-creator/skills/skill-creator/assets/eval_review.html +146 -0
- handler_control/data/builtin-agents/skill-creator/skills/skill-creator/eval-viewer/generate_review.py +471 -0
- handler_control/data/builtin-agents/skill-creator/skills/skill-creator/eval-viewer/viewer.html +1325 -0
- handler_control/data/builtin-agents/skill-creator/skills/skill-creator/references/schemas.md +430 -0
- handler_control/data/builtin-agents/skill-creator/skills/skill-creator/scripts/__init__.py +0 -0
- handler_control/data/builtin-agents/skill-creator/skills/skill-creator/scripts/aggregate_benchmark.py +401 -0
- handler_control/data/builtin-agents/skill-creator/skills/skill-creator/scripts/generate_report.py +326 -0
- handler_control/data/builtin-agents/skill-creator/skills/skill-creator/scripts/improve_description.py +247 -0
- handler_control/data/builtin-agents/skill-creator/skills/skill-creator/scripts/package_skill.py +136 -0
- handler_control/data/builtin-agents/skill-creator/skills/skill-creator/scripts/quick_validate.py +103 -0
- handler_control/data/builtin-agents/skill-creator/skills/skill-creator/scripts/run_eval.py +310 -0
- handler_control/data/builtin-agents/skill-creator/skills/skill-creator/scripts/run_loop.py +328 -0
- handler_control/data/builtin-agents/skill-creator/skills/skill-creator/scripts/utils.py +47 -0
- handler_control/data/builtin-agents/skill-review/agent.toml +13 -0
- handler_control/data/builtin-agents/skill-review/instructions.md +30 -0
- handler_control/data/builtin-agents/skill-review/interface.json +83 -0
- handler_control/data/interfaces/llm/v1/interface.toml +17 -0
- handler_control/data/interfaces/llm/v1/tools.json +138 -0
- handler_control/data/templates/README.md +10 -0
- handler_control/data/templates/providers/anthropic-api.toml +18 -0
- handler_control/data/templates/providers/llamacpp.toml +210 -0
- handler_control/data/templates/providers/mistral-api.toml +14 -0
- handler_control/data/templates/providers/ollama.toml +10 -0
- handler_control/data/templates/providers/openai-api.toml +13 -0
- handler_control/data/templates/providers/openrouter.toml +10 -0
- handler_control/desktop.py +68 -0
- handler_control/display.py +62 -0
- handler_control/files.py +23 -0
- handler_control/folders.py +68 -0
- handler_control/git_read.py +39 -0
- handler_control/image_check.py +322 -0
- handler_control/image_updates.py +174 -0
- handler_control/inbox.py +70 -0
- handler_control/interfaces.py +151 -0
- handler_control/knowledge.py +237 -0
- handler_control/launcher.py +356 -0
- handler_control/llm.py +194 -0
- handler_control/migrations/__init__.py +62 -0
- handler_control/policy.py +305 -0
- handler_control/projects.py +266 -0
- handler_control/providers.py +180 -0
- handler_control/relay.py +133 -0
- handler_control/remote.py +888 -0
- handler_control/remote_crypto.py +97 -0
- handler_control/runtime.py +140 -0
- handler_control/sandbox_process.py +224 -0
- handler_control/schedules.py +58 -0
- handler_control/secrets.py +242 -0
- handler_control/service_router.py +180 -0
- handler_control/services.py +92 -0
- handler_control/session_files.py +429 -0
- handler_control/sessions.py +1116 -0
- handler_control/settings.py +129 -0
- handler_control/shortcuts.py +80 -0
- handler_control/skill_scan.py +88 -0
- handler_control/skill_sources.py +61 -0
- handler_control/skills.py +378 -0
- handler_control/terminal.py +79 -0
- handler_control/terminals.py +108 -0
- handler_control/transcript.py +174 -0
- handler_control/web-ui/assets/index-1w5RPsfA.js +190 -0
- handler_control/web-ui/assets/index-dWnzCLzQ.css +1 -0
- handler_control/web-ui/assets/inter-cyrillic-ext-wght-normal-BOeWTOD4.woff2 +0 -0
- handler_control/web-ui/assets/inter-cyrillic-wght-normal-DqGufNeO.woff2 +0 -0
- handler_control/web-ui/assets/inter-greek-ext-wght-normal-DlzME5K_.woff2 +0 -0
- handler_control/web-ui/assets/inter-greek-wght-normal-CkhJZR-_.woff2 +0 -0
- handler_control/web-ui/assets/inter-latin-ext-wght-normal-DO1Apj_S.woff2 +0 -0
- handler_control/web-ui/assets/inter-latin-wght-normal-Dx4kXJAl.woff2 +0 -0
- handler_control/web-ui/assets/inter-vietnamese-wght-normal-CBcvBZtf.woff2 +0 -0
- handler_control/web-ui/favicon.svg +1 -0
- handler_control/web-ui/index.html +15 -0
- handler_control/webpush.py +90 -0
- handler_control/webui.py +105 -0
- handler_harness/__init__.py +13 -0
- handler_harness/addons.py +129 -0
- handler_harness/catalog.py +116 -0
- handler_harness/cli.py +109 -0
- handler_harness/data/addons/README.md +44 -0
- handler_harness/data/addons/miniapp/Containerfile +20 -0
- handler_harness/data/addons/miniapp/PROVISIONING.md +16 -0
- handler_harness/data/addons/miniapp/addon.toml +16 -0
- handler_harness/data/addons/miniapp/skills/miniapp/SKILL.md +140 -0
- handler_harness/data/addons/miniapp/skills/miniapp/template/.gitignore +2 -0
- handler_harness/data/addons/miniapp/skills/miniapp/template/app.toml +15 -0
- handler_harness/data/addons/miniapp/skills/miniapp/template/server/db.ts +30 -0
- handler_harness/data/addons/miniapp/skills/miniapp/template/server/deno.json +7 -0
- handler_harness/data/addons/miniapp/skills/miniapp/template/server/handler.ts +26 -0
- handler_harness/data/addons/miniapp/skills/miniapp/template/server/main.ts +18 -0
- handler_harness/data/addons/miniapp/skills/miniapp/template/server/main_test.ts +32 -0
- handler_harness/data/addons/miniapp/skills/miniapp/template/server/migrations/001_notes.sql +6 -0
- handler_harness/data/addons/miniapp/skills/miniapp/template/server/routes.ts +32 -0
- handler_harness/data/addons/miniapp/skills/miniapp/template/ui/deno.json +12 -0
- handler_harness/data/addons/miniapp/skills/miniapp/template/ui/index.html +12 -0
- handler_harness/data/addons/miniapp/skills/miniapp/template/ui/src/App.svelte +44 -0
- handler_harness/data/addons/miniapp/skills/miniapp/template/ui/src/api.ts +16 -0
- handler_harness/data/addons/miniapp/skills/miniapp/template/ui/src/app.css +24 -0
- handler_harness/data/addons/miniapp/skills/miniapp/template/ui/src/main.ts +5 -0
- handler_harness/data/addons/miniapp/skills/miniapp/template/ui/vite.config.ts +17 -0
- handler_harness/data/catalog/README.md +12 -0
- handler_harness/data/catalog/playwright/Containerfile +28 -0
- handler_harness/data/catalog/playwright/PROVISIONING.md +53 -0
- handler_harness/data/catalog/playwright/addon.toml +25 -0
- handler_harness/data/catalog/playwright/handler/playwright-mcp-check.mjs +23 -0
- handler_harness/data/catalog/playwright/handler/playwright-mcp.mjs +61 -0
- handler_harness/data/catalog/playwright/skills/browser/SKILL.md +41 -0
- handler_harness/data/catalog/shopify/Containerfile +22 -0
- handler_harness/data/catalog/shopify/PROVISIONING.md +49 -0
- handler_harness/data/catalog/shopify/addon.toml +39 -0
- handler_harness/data/catalog/shopify/skills/shopify/SKILL.md +41 -0
- handler_harness/data/handler-sandbox-env +30 -0
- handler_harness/data/harnesses/README.md +47 -0
- handler_harness/data/harnesses/claude-code/Containerfile +27 -0
- handler_harness/data/harnesses/claude-code/harness.toml +95 -0
- handler_harness/data/harnesses/codex/Containerfile +26 -0
- handler_harness/data/harnesses/codex/harness.toml +103 -0
- handler_harness/data/harnesses/pi/Containerfile +27 -0
- handler_harness/data/harnesses/pi/harness.toml +65 -0
- handler_harness/data/harnesses/vibe/Containerfile +19 -0
- handler_harness/data/harnesses/vibe/harness.toml +96 -0
- handler_harness/data/toolbox/Containerfile +56 -0
- handler_harness/data/toolbox/README.md +24 -0
- handler_harness/data/toolbox/toolbox.toml +41 -0
- handler_harness/images.py +62 -0
- handler_harness/providers.py +300 -0
- handler_harness/render.py +472 -0
- handler_sandbox_podman/__init__.py +1 -0
- handler_sandbox_podman/caches.py +97 -0
- handler_sandbox_podman/check.py +121 -0
- handler_sandbox_podman/cli.py +239 -0
- handler_sandbox_podman/egress.py +118 -0
- handler_sandbox_podman/images.py +199 -0
- handler_sandbox_podman/launch.py +474 -0
- handler_sandbox_podman/podman.py +108 -0
- handler_sandbox_podman/proxy/Containerfile +6 -0
- handler_sandbox_podman/proxy/addon.py +255 -0
|
@@ -0,0 +1,229 @@
|
|
|
1
|
+
"""Building blocks shared by several contracts (docs/dev/contracts.md)."""
|
|
2
|
+
|
|
3
|
+
import re
|
|
4
|
+
from pathlib import Path
|
|
5
|
+
from typing import Annotated, Literal
|
|
6
|
+
from urllib.parse import urlparse
|
|
7
|
+
|
|
8
|
+
from pydantic import AfterValidator, BaseModel, BeforeValidator, ConfigDict, Field
|
|
9
|
+
from pydantic.alias_generators import to_camel
|
|
10
|
+
|
|
11
|
+
|
|
12
|
+
def _absolute_path(p: str) -> str:
|
|
13
|
+
if not p.startswith("/"):
|
|
14
|
+
raise ValueError("must be an absolute path")
|
|
15
|
+
if p != "/" and any(seg in ("", ".", "..") for seg in p[1:].split("/")):
|
|
16
|
+
raise ValueError("must be normalized (no empty, '.' or '..' segments, no trailing slash)")
|
|
17
|
+
return p
|
|
18
|
+
|
|
19
|
+
|
|
20
|
+
def _expand_home(p: object) -> object:
|
|
21
|
+
"""'~' and '~/rel' → the home of the user reading the document (the daemon's user)."""
|
|
22
|
+
if isinstance(p, str) and (p == "~" or p.startswith("~/")):
|
|
23
|
+
return str(Path.home()) + p[1:]
|
|
24
|
+
return p
|
|
25
|
+
|
|
26
|
+
|
|
27
|
+
def _home_path(p: str) -> str:
|
|
28
|
+
if not (p == "~" or p.startswith("~/") or p.startswith("/")):
|
|
29
|
+
raise ValueError("must be absolute or start with '~/'")
|
|
30
|
+
if any(seg in (".", "..") for seg in p.split("/")[1:]):
|
|
31
|
+
raise ValueError("must not contain '.' or '..' segments")
|
|
32
|
+
return p
|
|
33
|
+
|
|
34
|
+
|
|
35
|
+
_EGRESS_HOST_RE = re.compile(r"^(\*\.)?([a-z0-9]([a-z0-9-]{0,61}[a-z0-9])?\.)+[a-z]{2,63}$")
|
|
36
|
+
_OCTET = r"(25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)"
|
|
37
|
+
_IPV4_RE = re.compile(rf"^({_OCTET}\.){{3}}{_OCTET}$")
|
|
38
|
+
_PORT_RE = re.compile(r"^([1-9]\d{0,3}|[1-5]\d{4}|6[0-4]\d{3}|65[0-4]\d\d|655[0-2]\d|6553[0-5])$")
|
|
39
|
+
|
|
40
|
+
|
|
41
|
+
def _egress_entry(e: str) -> str:
|
|
42
|
+
host, sep, port = e.rpartition(":")
|
|
43
|
+
if not (sep and _PORT_RE.match(port) and (_EGRESS_HOST_RE.match(host) or _IPV4_RE.match(host))):
|
|
44
|
+
raise ValueError(
|
|
45
|
+
"must be HOST:PORT, HOST a lowercase hostname (optionally prefixed with '*.') or an "
|
|
46
|
+
"IPv4 address, and an explicit port (443 for https, 80 for http)"
|
|
47
|
+
)
|
|
48
|
+
return e
|
|
49
|
+
|
|
50
|
+
|
|
51
|
+
def egress_target(url: str) -> str:
|
|
52
|
+
"""The allow-list entry a URL needs: its lowercased host and port, the scheme's default port
|
|
53
|
+
(https 443, http 80) when the URL has none."""
|
|
54
|
+
parsed = urlparse(url)
|
|
55
|
+
port = parsed.port or {"https": 443, "http": 80}[parsed.scheme]
|
|
56
|
+
return f"{(parsed.hostname or '').lower()}:{port}"
|
|
57
|
+
|
|
58
|
+
|
|
59
|
+
AbsolutePath = Annotated[str, AfterValidator(_absolute_path)]
|
|
60
|
+
"""Absolute, normalized path: starts with '/', no '.', '..' or empty segments."""
|
|
61
|
+
|
|
62
|
+
HostPath = Annotated[str, BeforeValidator(_expand_home), AfterValidator(_absolute_path)]
|
|
63
|
+
"""Host path in a hand-written document (providers, bindings, mini app manifests): an
|
|
64
|
+
AbsolutePath, or '~' / '~/rel' for the host user's home, expanded when the document is read."""
|
|
65
|
+
|
|
66
|
+
HomePath = Annotated[str, AfterValidator(_home_path)]
|
|
67
|
+
"""Sandbox path that may be relative to the harness home: '/abs', '~' or '~/rel'."""
|
|
68
|
+
|
|
69
|
+
EgressEntry = Annotated[str, AfterValidator(_egress_entry)]
|
|
70
|
+
"""Allow-list entry (A.3), always with its port: 'example.com:443' exact host, '*.example.com:443'
|
|
71
|
+
subdomains only, '192.168.1.20:11434' an IPv4 address (e.g. a model server on the LAN). No default
|
|
72
|
+
port: https is 443, http 80, and any other port is written out."""
|
|
73
|
+
|
|
74
|
+
EnvName = Annotated[str, Field(pattern=r"^[A-Za-z_][A-Za-z0-9_]*$")]
|
|
75
|
+
EnvMap = dict[EnvName, str]
|
|
76
|
+
|
|
77
|
+
PermissionMode = Literal["ask", "accept-edits", "bypass"]
|
|
78
|
+
PERMISSION_MODES: tuple[PermissionMode, ...] = ("ask", "accept-edits", "bypass")
|
|
79
|
+
NetworkMode = Literal["none", "filtered", "open"]
|
|
80
|
+
# D.8: a project's agents use the global caches (with other projects), their own, or none
|
|
81
|
+
CacheSharing = Literal["global", "project", "off"]
|
|
82
|
+
Enforcement = Literal["enforce", "log-only"]
|
|
83
|
+
Confinement = Literal["strict", "nested", "privileged"]
|
|
84
|
+
"""How locked down a container is (A.5): "strict" = the A3 hardening; "nested" = what running
|
|
85
|
+
containers inside it needs (podman-in-podman); "privileged" = no confinement beyond the user
|
|
86
|
+
namespace, about as safe as no sandbox."""
|
|
87
|
+
|
|
88
|
+
|
|
89
|
+
class Document(BaseModel):
|
|
90
|
+
"""Exchanged document: camelCase on the wire, unknown fields ignored (contracts §0)."""
|
|
91
|
+
|
|
92
|
+
model_config = ConfigDict(
|
|
93
|
+
alias_generator=to_camel,
|
|
94
|
+
populate_by_name=True,
|
|
95
|
+
extra="ignore",
|
|
96
|
+
frozen=True,
|
|
97
|
+
)
|
|
98
|
+
|
|
99
|
+
|
|
100
|
+
ResourceName = Annotated[str, Field(min_length=1, pattern=r"^[a-z0-9][a-z0-9_.-]*$")]
|
|
101
|
+
"""The name of an account, a provider or a binding: its file's stem."""
|
|
102
|
+
|
|
103
|
+
ANY_CONTEXT = "any"
|
|
104
|
+
"""In an account's or a provider's contexts: usable in every project (a local model)."""
|
|
105
|
+
|
|
106
|
+
|
|
107
|
+
def _not_any(name: str) -> str:
|
|
108
|
+
if name.lower() == ANY_CONTEXT:
|
|
109
|
+
raise ValueError(f"'{ANY_CONTEXT}' is not a context's name: it means every context")
|
|
110
|
+
return name
|
|
111
|
+
|
|
112
|
+
|
|
113
|
+
_CONTEXT = Field(min_length=1, max_length=32, pattern=r"^[A-Za-z0-9]([A-Za-z0-9 _-]*[A-Za-z0-9])?$")
|
|
114
|
+
ContextName = Annotated[str, _CONTEXT, AfterValidator(_not_any)]
|
|
115
|
+
"""A project context (D.19): "Work", "Personal", a client's name."""
|
|
116
|
+
AccessContext = Annotated[str, _CONTEXT]
|
|
117
|
+
"""Where an account or a provider may be used (D.19): a context's name, or `any`."""
|
|
118
|
+
ContextColor = Literal["blue", "violet", "cyan", "pink", "slate"]
|
|
119
|
+
"""A context's colour (D.19): never the accent's nor a status's (orange, green, amber, red)."""
|
|
120
|
+
|
|
121
|
+
|
|
122
|
+
class StrictDocument(Document):
|
|
123
|
+
"""Hand-written document (accounts, providers, bindings, mini app manifests): unknown keys
|
|
124
|
+
are errors (typo protection)."""
|
|
125
|
+
|
|
126
|
+
model_config = ConfigDict(extra="forbid")
|
|
127
|
+
|
|
128
|
+
|
|
129
|
+
class EnvSecret(StrictDocument):
|
|
130
|
+
env: EnvName
|
|
131
|
+
|
|
132
|
+
|
|
133
|
+
class FileSecret(StrictDocument):
|
|
134
|
+
file: HostPath
|
|
135
|
+
|
|
136
|
+
|
|
137
|
+
class CommandSecret(StrictDocument):
|
|
138
|
+
command: Annotated[list[Annotated[str, Field(min_length=1)]], Field(min_length=1)]
|
|
139
|
+
|
|
140
|
+
|
|
141
|
+
# A value in handler's secret store (D.14), named after what owns it: `providers/openrouter/apiKey`,
|
|
142
|
+
# `apps/notes/TOKEN`. Segments never start with a dot.
|
|
143
|
+
SecretPath = Annotated[
|
|
144
|
+
str,
|
|
145
|
+
Field(
|
|
146
|
+
max_length=256,
|
|
147
|
+
pattern=r"^[A-Za-z0-9_-][A-Za-z0-9_.-]*(/[A-Za-z0-9_-][A-Za-z0-9_.-]*)*$",
|
|
148
|
+
),
|
|
149
|
+
]
|
|
150
|
+
|
|
151
|
+
|
|
152
|
+
class StoreSecret(StrictDocument):
|
|
153
|
+
secret: SecretPath
|
|
154
|
+
|
|
155
|
+
|
|
156
|
+
SecretRef = EnvSecret | FileSecret | CommandSecret | StoreSecret
|
|
157
|
+
"""Reference to a secret, never the value itself (E2)."""
|
|
158
|
+
|
|
159
|
+
|
|
160
|
+
class FileContent(StrictDocument):
|
|
161
|
+
content: str
|
|
162
|
+
|
|
163
|
+
|
|
164
|
+
FileSource = FileSecret | EnvSecret | FileContent
|
|
165
|
+
"""Where a config file's content comes from: a host file (`file`), an environment variable of
|
|
166
|
+
the daemon (`env`), or inline in the provider (`content`)."""
|
|
167
|
+
|
|
168
|
+
|
|
169
|
+
# app: H.3; subject: the handler object a run works on (D.18), read-only
|
|
170
|
+
MountKind = Literal["scope", "state", "cache", "config", "knowledge", "app", "subject"]
|
|
171
|
+
|
|
172
|
+
|
|
173
|
+
class Mount(Document):
|
|
174
|
+
"""Filesystem mount as seen by the sandbox (A.1). Targets are absolute."""
|
|
175
|
+
|
|
176
|
+
source: AbsolutePath
|
|
177
|
+
target: AbsolutePath
|
|
178
|
+
read_only: bool
|
|
179
|
+
kind: MountKind
|
|
180
|
+
|
|
181
|
+
|
|
182
|
+
class GeneratedFile(Document):
|
|
183
|
+
"""File generated by the platform: mounted read-only into the sandbox, or — with `seed` —
|
|
184
|
+
copied into a writable path before the process starts, so the harness may rewrite it
|
|
185
|
+
(changes are discarded with the sandbox). A seeded file must lie under a writable path."""
|
|
186
|
+
|
|
187
|
+
target: AbsolutePath
|
|
188
|
+
content: str
|
|
189
|
+
mode: Annotated[str, Field(pattern=r"^0?[0-7]{3}$")] | None = None
|
|
190
|
+
seed: bool = False
|
|
191
|
+
|
|
192
|
+
|
|
193
|
+
class NameValue(StrictDocument):
|
|
194
|
+
name: str
|
|
195
|
+
value: str
|
|
196
|
+
|
|
197
|
+
|
|
198
|
+
class StdioMcpServer(StrictDocument):
|
|
199
|
+
name: Annotated[str, Field(min_length=1)]
|
|
200
|
+
command: Annotated[str, Field(min_length=1)]
|
|
201
|
+
args: list[str] = []
|
|
202
|
+
env: list[NameValue] = []
|
|
203
|
+
|
|
204
|
+
|
|
205
|
+
class HttpMcpServer(StrictDocument):
|
|
206
|
+
type: Literal["http", "sse"]
|
|
207
|
+
name: Annotated[str, Field(min_length=1)]
|
|
208
|
+
url: Annotated[str, Field(pattern=r"^https?://")]
|
|
209
|
+
headers: list[NameValue] = []
|
|
210
|
+
|
|
211
|
+
|
|
212
|
+
McpServer = StdioMcpServer | HttpMcpServer
|
|
213
|
+
"""MCP server, same shape as ACP `session/new` `mcpServers` entries."""
|
|
214
|
+
|
|
215
|
+
McpTransport = Literal["stdio", "http", "sse"]
|
|
216
|
+
|
|
217
|
+
|
|
218
|
+
def mcp_transport(server: McpServer) -> McpTransport:
|
|
219
|
+
return "stdio" if isinstance(server, StdioMcpServer) else server.type
|
|
220
|
+
|
|
221
|
+
|
|
222
|
+
class Resources(StrictDocument):
|
|
223
|
+
memory: Annotated[str, Field(pattern=r"^\d+[kmgKMG]?$")] | None = None
|
|
224
|
+
cpus: Annotated[str, Field(pattern=r"^\d+(\.\d+)?$")] | None = None
|
|
225
|
+
|
|
226
|
+
|
|
227
|
+
def is_within(path: str, root: str) -> bool:
|
|
228
|
+
"""True when `path` equals `root` or is below it (both already normalized)."""
|
|
229
|
+
return path == root or path.startswith("/" if root == "/" else f"{root}/")
|