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.
Files changed (234) hide show
  1. handler_ai-0.1.0.dist-info/METADATA +60 -0
  2. handler_ai-0.1.0.dist-info/RECORD +234 -0
  3. handler_ai-0.1.0.dist-info/WHEEL +4 -0
  4. handler_ai-0.1.0.dist-info/entry_points.txt +6 -0
  5. handler_cli/__init__.py +1 -0
  6. handler_cli/account_commands.py +314 -0
  7. handler_cli/addon_commands.py +128 -0
  8. handler_cli/agent_commands.py +139 -0
  9. handler_cli/app_commands.py +175 -0
  10. handler_cli/app_window.py +85 -0
  11. handler_cli/automation_commands.py +309 -0
  12. handler_cli/bridge.py +42 -0
  13. handler_cli/cli.py +326 -0
  14. handler_cli/client.py +81 -0
  15. handler_cli/daemon_commands.py +108 -0
  16. handler_cli/image_commands.py +102 -0
  17. handler_cli/inbox_commands.py +33 -0
  18. handler_cli/output.py +12 -0
  19. handler_cli/project_commands.py +63 -0
  20. handler_cli/provider_commands.py +113 -0
  21. handler_cli/secret_commands.py +64 -0
  22. handler_cli/session_bridge.py +93 -0
  23. handler_cli/session_commands.py +485 -0
  24. handler_cli/setup.py +120 -0
  25. handler_cli/terminal.py +280 -0
  26. handler_cli/terminal_client.py +37 -0
  27. handler_contracts/__init__.py +642 -0
  28. handler_contracts/account.py +36 -0
  29. handler_contracts/acp_endpoint.py +69 -0
  30. handler_contracts/addon.py +147 -0
  31. handler_contracts/agent_spec.py +75 -0
  32. handler_contracts/automation.py +285 -0
  33. handler_contracts/common.py +229 -0
  34. handler_contracts/control.py +1208 -0
  35. handler_contracts/harness.py +300 -0
  36. handler_contracts/miniapp.py +141 -0
  37. handler_contracts/provider.py +82 -0
  38. handler_contracts/remote.py +167 -0
  39. handler_contracts/sandbox.py +199 -0
  40. handler_contracts/schemas.py +313 -0
  41. handler_contracts/terminal.py +32 -0
  42. handler_control/__init__.py +7 -0
  43. handler_control/access.py +62 -0
  44. handler_control/accounts.py +279 -0
  45. handler_control/addon_install.py +268 -0
  46. handler_control/addons.py +118 -0
  47. handler_control/agent_state.py +46 -0
  48. handler_control/agents.py +257 -0
  49. handler_control/api/__init__.py +137 -0
  50. handler_control/api/accounts.py +231 -0
  51. handler_control/api/acp.py +116 -0
  52. handler_control/api/addons.py +95 -0
  53. handler_control/api/agents.py +150 -0
  54. handler_control/api/apps.py +104 -0
  55. handler_control/api/automations.py +128 -0
  56. handler_control/api/bindings.py +105 -0
  57. handler_control/api/builtin_agents.py +22 -0
  58. handler_control/api/caches.py +79 -0
  59. handler_control/api/catalog.py +197 -0
  60. handler_control/api/common.py +205 -0
  61. handler_control/api/contexts.py +88 -0
  62. handler_control/api/folders.py +51 -0
  63. handler_control/api/images.py +164 -0
  64. handler_control/api/inbox.py +21 -0
  65. handler_control/api/projects.py +170 -0
  66. handler_control/api/providers.py +107 -0
  67. handler_control/api/remote.py +45 -0
  68. handler_control/api/secrets.py +177 -0
  69. handler_control/api/sessions.py +192 -0
  70. handler_control/api/skills.py +270 -0
  71. handler_control/app_api.py +84 -0
  72. handler_control/app_http.py +214 -0
  73. handler_control/app_remote.py +206 -0
  74. handler_control/apps.py +867 -0
  75. handler_control/auth.py +48 -0
  76. handler_control/automation_config.py +255 -0
  77. handler_control/automations.py +1134 -0
  78. handler_control/bindings.py +149 -0
  79. handler_control/builds.py +341 -0
  80. handler_control/builtin_agents.py +82 -0
  81. handler_control/caches.py +113 -0
  82. handler_control/checks.py +287 -0
  83. handler_control/compose.py +317 -0
  84. handler_control/config_files.py +41 -0
  85. handler_control/contexts.py +75 -0
  86. handler_control/daemon.py +64 -0
  87. handler_control/data/automation/bin/handler-llm +85 -0
  88. handler_control/data/automations/env-issues/automation.toml +17 -0
  89. handler_control/data/automations/env-issues/extract.py +73 -0
  90. handler_control/data/automations/env-issues/findings.schema.json +28 -0
  91. handler_control/data/automations/env-issues/prompt.md +19 -0
  92. handler_control/data/builtin-agents/skill-creator/agent.toml +10 -0
  93. handler_control/data/builtin-agents/skill-creator/instructions.md +5 -0
  94. handler_control/data/builtin-agents/skill-creator/skills/handler-skills/SKILL.md +44 -0
  95. handler_control/data/builtin-agents/skill-creator/skills/skill-creator/LICENSE.txt +202 -0
  96. handler_control/data/builtin-agents/skill-creator/skills/skill-creator/SKILL.md +485 -0
  97. handler_control/data/builtin-agents/skill-creator/skills/skill-creator/agents/analyzer.md +274 -0
  98. handler_control/data/builtin-agents/skill-creator/skills/skill-creator/agents/comparator.md +202 -0
  99. handler_control/data/builtin-agents/skill-creator/skills/skill-creator/agents/grader.md +223 -0
  100. handler_control/data/builtin-agents/skill-creator/skills/skill-creator/assets/eval_review.html +146 -0
  101. handler_control/data/builtin-agents/skill-creator/skills/skill-creator/eval-viewer/generate_review.py +471 -0
  102. handler_control/data/builtin-agents/skill-creator/skills/skill-creator/eval-viewer/viewer.html +1325 -0
  103. handler_control/data/builtin-agents/skill-creator/skills/skill-creator/references/schemas.md +430 -0
  104. handler_control/data/builtin-agents/skill-creator/skills/skill-creator/scripts/__init__.py +0 -0
  105. handler_control/data/builtin-agents/skill-creator/skills/skill-creator/scripts/aggregate_benchmark.py +401 -0
  106. handler_control/data/builtin-agents/skill-creator/skills/skill-creator/scripts/generate_report.py +326 -0
  107. handler_control/data/builtin-agents/skill-creator/skills/skill-creator/scripts/improve_description.py +247 -0
  108. handler_control/data/builtin-agents/skill-creator/skills/skill-creator/scripts/package_skill.py +136 -0
  109. handler_control/data/builtin-agents/skill-creator/skills/skill-creator/scripts/quick_validate.py +103 -0
  110. handler_control/data/builtin-agents/skill-creator/skills/skill-creator/scripts/run_eval.py +310 -0
  111. handler_control/data/builtin-agents/skill-creator/skills/skill-creator/scripts/run_loop.py +328 -0
  112. handler_control/data/builtin-agents/skill-creator/skills/skill-creator/scripts/utils.py +47 -0
  113. handler_control/data/builtin-agents/skill-review/agent.toml +13 -0
  114. handler_control/data/builtin-agents/skill-review/instructions.md +30 -0
  115. handler_control/data/builtin-agents/skill-review/interface.json +83 -0
  116. handler_control/data/interfaces/llm/v1/interface.toml +17 -0
  117. handler_control/data/interfaces/llm/v1/tools.json +138 -0
  118. handler_control/data/templates/README.md +10 -0
  119. handler_control/data/templates/providers/anthropic-api.toml +18 -0
  120. handler_control/data/templates/providers/llamacpp.toml +210 -0
  121. handler_control/data/templates/providers/mistral-api.toml +14 -0
  122. handler_control/data/templates/providers/ollama.toml +10 -0
  123. handler_control/data/templates/providers/openai-api.toml +13 -0
  124. handler_control/data/templates/providers/openrouter.toml +10 -0
  125. handler_control/desktop.py +68 -0
  126. handler_control/display.py +62 -0
  127. handler_control/files.py +23 -0
  128. handler_control/folders.py +68 -0
  129. handler_control/git_read.py +39 -0
  130. handler_control/image_check.py +322 -0
  131. handler_control/image_updates.py +174 -0
  132. handler_control/inbox.py +70 -0
  133. handler_control/interfaces.py +151 -0
  134. handler_control/knowledge.py +237 -0
  135. handler_control/launcher.py +356 -0
  136. handler_control/llm.py +194 -0
  137. handler_control/migrations/__init__.py +62 -0
  138. handler_control/policy.py +305 -0
  139. handler_control/projects.py +266 -0
  140. handler_control/providers.py +180 -0
  141. handler_control/relay.py +133 -0
  142. handler_control/remote.py +888 -0
  143. handler_control/remote_crypto.py +97 -0
  144. handler_control/runtime.py +140 -0
  145. handler_control/sandbox_process.py +224 -0
  146. handler_control/schedules.py +58 -0
  147. handler_control/secrets.py +242 -0
  148. handler_control/service_router.py +180 -0
  149. handler_control/services.py +92 -0
  150. handler_control/session_files.py +429 -0
  151. handler_control/sessions.py +1116 -0
  152. handler_control/settings.py +129 -0
  153. handler_control/shortcuts.py +80 -0
  154. handler_control/skill_scan.py +88 -0
  155. handler_control/skill_sources.py +61 -0
  156. handler_control/skills.py +378 -0
  157. handler_control/terminal.py +79 -0
  158. handler_control/terminals.py +108 -0
  159. handler_control/transcript.py +174 -0
  160. handler_control/web-ui/assets/index-1w5RPsfA.js +190 -0
  161. handler_control/web-ui/assets/index-dWnzCLzQ.css +1 -0
  162. handler_control/web-ui/assets/inter-cyrillic-ext-wght-normal-BOeWTOD4.woff2 +0 -0
  163. handler_control/web-ui/assets/inter-cyrillic-wght-normal-DqGufNeO.woff2 +0 -0
  164. handler_control/web-ui/assets/inter-greek-ext-wght-normal-DlzME5K_.woff2 +0 -0
  165. handler_control/web-ui/assets/inter-greek-wght-normal-CkhJZR-_.woff2 +0 -0
  166. handler_control/web-ui/assets/inter-latin-ext-wght-normal-DO1Apj_S.woff2 +0 -0
  167. handler_control/web-ui/assets/inter-latin-wght-normal-Dx4kXJAl.woff2 +0 -0
  168. handler_control/web-ui/assets/inter-vietnamese-wght-normal-CBcvBZtf.woff2 +0 -0
  169. handler_control/web-ui/favicon.svg +1 -0
  170. handler_control/web-ui/index.html +15 -0
  171. handler_control/webpush.py +90 -0
  172. handler_control/webui.py +105 -0
  173. handler_harness/__init__.py +13 -0
  174. handler_harness/addons.py +129 -0
  175. handler_harness/catalog.py +116 -0
  176. handler_harness/cli.py +109 -0
  177. handler_harness/data/addons/README.md +44 -0
  178. handler_harness/data/addons/miniapp/Containerfile +20 -0
  179. handler_harness/data/addons/miniapp/PROVISIONING.md +16 -0
  180. handler_harness/data/addons/miniapp/addon.toml +16 -0
  181. handler_harness/data/addons/miniapp/skills/miniapp/SKILL.md +140 -0
  182. handler_harness/data/addons/miniapp/skills/miniapp/template/.gitignore +2 -0
  183. handler_harness/data/addons/miniapp/skills/miniapp/template/app.toml +15 -0
  184. handler_harness/data/addons/miniapp/skills/miniapp/template/server/db.ts +30 -0
  185. handler_harness/data/addons/miniapp/skills/miniapp/template/server/deno.json +7 -0
  186. handler_harness/data/addons/miniapp/skills/miniapp/template/server/handler.ts +26 -0
  187. handler_harness/data/addons/miniapp/skills/miniapp/template/server/main.ts +18 -0
  188. handler_harness/data/addons/miniapp/skills/miniapp/template/server/main_test.ts +32 -0
  189. handler_harness/data/addons/miniapp/skills/miniapp/template/server/migrations/001_notes.sql +6 -0
  190. handler_harness/data/addons/miniapp/skills/miniapp/template/server/routes.ts +32 -0
  191. handler_harness/data/addons/miniapp/skills/miniapp/template/ui/deno.json +12 -0
  192. handler_harness/data/addons/miniapp/skills/miniapp/template/ui/index.html +12 -0
  193. handler_harness/data/addons/miniapp/skills/miniapp/template/ui/src/App.svelte +44 -0
  194. handler_harness/data/addons/miniapp/skills/miniapp/template/ui/src/api.ts +16 -0
  195. handler_harness/data/addons/miniapp/skills/miniapp/template/ui/src/app.css +24 -0
  196. handler_harness/data/addons/miniapp/skills/miniapp/template/ui/src/main.ts +5 -0
  197. handler_harness/data/addons/miniapp/skills/miniapp/template/ui/vite.config.ts +17 -0
  198. handler_harness/data/catalog/README.md +12 -0
  199. handler_harness/data/catalog/playwright/Containerfile +28 -0
  200. handler_harness/data/catalog/playwright/PROVISIONING.md +53 -0
  201. handler_harness/data/catalog/playwright/addon.toml +25 -0
  202. handler_harness/data/catalog/playwright/handler/playwright-mcp-check.mjs +23 -0
  203. handler_harness/data/catalog/playwright/handler/playwright-mcp.mjs +61 -0
  204. handler_harness/data/catalog/playwright/skills/browser/SKILL.md +41 -0
  205. handler_harness/data/catalog/shopify/Containerfile +22 -0
  206. handler_harness/data/catalog/shopify/PROVISIONING.md +49 -0
  207. handler_harness/data/catalog/shopify/addon.toml +39 -0
  208. handler_harness/data/catalog/shopify/skills/shopify/SKILL.md +41 -0
  209. handler_harness/data/handler-sandbox-env +30 -0
  210. handler_harness/data/harnesses/README.md +47 -0
  211. handler_harness/data/harnesses/claude-code/Containerfile +27 -0
  212. handler_harness/data/harnesses/claude-code/harness.toml +95 -0
  213. handler_harness/data/harnesses/codex/Containerfile +26 -0
  214. handler_harness/data/harnesses/codex/harness.toml +103 -0
  215. handler_harness/data/harnesses/pi/Containerfile +27 -0
  216. handler_harness/data/harnesses/pi/harness.toml +65 -0
  217. handler_harness/data/harnesses/vibe/Containerfile +19 -0
  218. handler_harness/data/harnesses/vibe/harness.toml +96 -0
  219. handler_harness/data/toolbox/Containerfile +56 -0
  220. handler_harness/data/toolbox/README.md +24 -0
  221. handler_harness/data/toolbox/toolbox.toml +41 -0
  222. handler_harness/images.py +62 -0
  223. handler_harness/providers.py +300 -0
  224. handler_harness/render.py +472 -0
  225. handler_sandbox_podman/__init__.py +1 -0
  226. handler_sandbox_podman/caches.py +97 -0
  227. handler_sandbox_podman/check.py +121 -0
  228. handler_sandbox_podman/cli.py +239 -0
  229. handler_sandbox_podman/egress.py +118 -0
  230. handler_sandbox_podman/images.py +199 -0
  231. handler_sandbox_podman/launch.py +474 -0
  232. handler_sandbox_podman/podman.py +108 -0
  233. handler_sandbox_podman/proxy/Containerfile +6 -0
  234. handler_sandbox_podman/proxy/addon.py +255 -0
@@ -0,0 +1,174 @@
1
+ """A session's event log (F.5), or a slice of it, as markdown for reading (I.4): what an
2
+ automation's script, and the model it calls, read of a conversation.
3
+
4
+ read the events of a log from one `seq` to another
5
+ render those events as a transcript: a header, then each event in order with its `seq`
6
+
7
+ Pure: no redaction here. The log is already redacted with the session's own secrets (F.5); the
8
+ caller redacts the result again with every value of the secret store before it leaves.
9
+ """
10
+
11
+ import contextlib
12
+ import json
13
+ import re
14
+ from collections.abc import Sequence
15
+ from pathlib import Path
16
+ from typing import Any
17
+
18
+ HEAD = 2000 # characters kept at the start of a tool call's input or output
19
+ TAIL = 1000 # and at its end
20
+ CHUNKS = {
21
+ "user_message_chunk": "Person",
22
+ "agent_message_chunk": "Agent",
23
+ "agent_thought_chunk": "Agent's thought",
24
+ }
25
+ TOOL_CALLS = ("tool_call", "tool_call_update")
26
+
27
+ Event = dict[str, Any]
28
+
29
+
30
+ def read(log: Path, seq_from: int, seq_to: int) -> list[Event]:
31
+ """The events of `log` with `seq_from <= seq <= seq_to`, in order (unreadable lines skipped)."""
32
+ events = []
33
+ with contextlib.suppress(FileNotFoundError):
34
+ for line in log.read_text().splitlines():
35
+ with contextlib.suppress(ValueError, KeyError, TypeError):
36
+ event = json.loads(line)
37
+ if seq_from <= event["seq"] <= seq_to:
38
+ events.append(event)
39
+ return events
40
+
41
+
42
+ def cut(text: str) -> str:
43
+ """The first HEAD and last TAIL characters, saying how many were left out between them."""
44
+ if len(text) <= HEAD + TAIL:
45
+ return text
46
+ left = len(text) - HEAD - TAIL
47
+ return f"{text[:HEAD]}\n… ({left} characters left out) …\n{text[-TAIL:]}"
48
+
49
+
50
+ def fenced(text: str, info: str = "") -> str:
51
+ """A code block that the text cannot close (its fence is longer than any run of backticks)."""
52
+ longest = max((len(m) for m in re.findall(r"`+", text)), default=0)
53
+ fence = "`" * max(3, longest + 1)
54
+ return f"{fence}{info}\n{text}\n{fence}"
55
+
56
+
57
+ def block_text(block: Any) -> str:
58
+ """One ACP content block, as text."""
59
+ if not isinstance(block, dict):
60
+ return ""
61
+ kind = block.get("type")
62
+ if kind == "text":
63
+ return str(block.get("text", ""))
64
+ if kind == "resource_link":
65
+ return f"[link: {block.get('uri', '')}]"
66
+ if kind == "resource":
67
+ resource = block.get("resource") or {}
68
+ return str(resource.get("text") or f"[resource: {resource.get('uri', '')}]")
69
+ return f"[{kind}]"
70
+
71
+
72
+ def tool_output(call: Event) -> str:
73
+ """A tool call's output: its raw output, else its content (text, diffs)."""
74
+ if call.get("rawOutput") is not None:
75
+ raw = call["rawOutput"]
76
+ return raw if isinstance(raw, str) else json.dumps(raw, indent=2)
77
+ parts = []
78
+ for item in call.get("content") or []:
79
+ if not isinstance(item, dict):
80
+ continue
81
+ if item.get("type") == "content":
82
+ parts.append(block_text(item.get("content")))
83
+ elif item.get("type") == "diff":
84
+ parts.append(f"diff of {item.get('path', '')}:\n{item.get('newText', '')}")
85
+ return "\n".join(p for p in parts if p)
86
+
87
+
88
+ def tool_call(seq: int, call: Event) -> str:
89
+ kind, status = call.get("kind"), call.get("status")
90
+ about = ", ".join(str(x) for x in (kind, status) if x)
91
+ lines = [f"### [{seq}] Tool call: {call.get('title') or call.get('toolCallId', '')}"]
92
+ if about:
93
+ lines[0] += f" ({about})"
94
+ if call.get("rawInput") is not None:
95
+ raw = call["rawInput"]
96
+ text = raw if isinstance(raw, str) else json.dumps(raw, indent=2)
97
+ lines.append("Input:\n" + fenced(cut(text)))
98
+ if output := tool_output(call):
99
+ lines.append("Output:\n" + fenced(cut(output)))
100
+ return "\n\n".join(lines)
101
+
102
+
103
+ def merged_calls(events: Sequence[Event]) -> dict[str, Event]:
104
+ """toolCallId → the call as it ended: its `tool_call` with its updates merged into it."""
105
+ calls: dict[str, Event] = {}
106
+ for event in events:
107
+ update = event.get("update") or {}
108
+ if event.get("kind") == "update" and update.get("sessionUpdate") in TOOL_CALLS:
109
+ call_id = str(update.get("toolCallId", ""))
110
+ merged = calls.get(call_id, {})
111
+ calls[call_id] = merged | {k: v for k, v in update.items() if v is not None}
112
+ return calls
113
+
114
+
115
+ def line(event: Event) -> str | None:
116
+ """An event that is neither a message chunk nor a tool call, or None when not shown."""
117
+ seq, kind, update = event.get("seq", 0), event.get("kind"), event.get("update") or {}
118
+ if kind == "update" and update.get("sessionUpdate") == "plan":
119
+ entries = update.get("entries") or []
120
+ items = "\n".join(f"- [{e.get('status', '')}] {e.get('content', '')}" for e in entries)
121
+ return f"[{seq}] Plan:\n{items}"
122
+ if kind == "turn" and event.get("phase") == "end":
123
+ return f"[{seq}] Turn ended ({event.get('stopReason', '')})"
124
+ if kind != "lifecycle":
125
+ return None
126
+ detail = event.get("detail", "")
127
+ how = f" ({event['how']})" if event.get("how") else ""
128
+ shown = {
129
+ "started": "Agent started",
130
+ "stopped": f"Session stopped{how}: {detail}",
131
+ "permission answered": f"Permission answered: {detail}",
132
+ }.get(event.get("event", ""))
133
+ return f"[{seq}] {shown}" if shown else None
134
+
135
+
136
+ def render(events: Sequence[Event], header: Sequence[tuple[str, str]]) -> str:
137
+ """The transcript: `header` (label, value) as a list, then the events. Message chunks of one
138
+ kind that follow each other are joined; a tool call is shown where it started, as it ended."""
139
+ calls = merged_calls(events)
140
+ parts = ["# Session transcript", "\n".join(f"- {label}: {value}" for label, value in header)]
141
+ chunk_kind: str | None = None
142
+ chunk_seq = 0
143
+ chunk: list[str] = []
144
+ shown: set[str] = set()
145
+
146
+ def flush() -> None:
147
+ nonlocal chunk_kind
148
+ if chunk_kind is not None and (text := "".join(chunk).strip()):
149
+ parts.append(f"## [{chunk_seq}] {CHUNKS[chunk_kind]}\n\n{text}")
150
+ chunk_kind = None
151
+ chunk.clear()
152
+
153
+ for event in events:
154
+ update = event.get("update") or {}
155
+ what = update.get("sessionUpdate") if event.get("kind") == "update" else None
156
+ if what in CHUNKS:
157
+ if what != chunk_kind:
158
+ flush()
159
+ chunk_kind, chunk_seq = what, event.get("seq", 0)
160
+ chunk.append(block_text(update.get("content")))
161
+ continue
162
+ shown_as = None
163
+ if what in TOOL_CALLS:
164
+ call_id = str(update.get("toolCallId", ""))
165
+ if call_id not in shown:
166
+ shown.add(call_id)
167
+ shown_as = tool_call(event.get("seq", 0), calls[call_id])
168
+ else:
169
+ shown_as = line(event)
170
+ if shown_as is not None:
171
+ flush()
172
+ parts.append(shown_as)
173
+ flush()
174
+ return "\n\n".join(parts) + "\n"