agent-lifecycle-kit 1.50.1__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 (227) hide show
  1. agent_lifecycle/__init__.py +5 -0
  2. agent_lifecycle/__main__.py +5 -0
  3. agent_lifecycle/_version.py +3 -0
  4. agent_lifecycle/adapter_sessions/__init__.py +41 -0
  5. agent_lifecycle/adapter_sessions/contracts.py +133 -0
  6. agent_lifecycle/adapter_sessions/env.py +44 -0
  7. agent_lifecycle/adapter_sessions/launcher.py +101 -0
  8. agent_lifecycle/adapter_sessions/process.py +39 -0
  9. agent_lifecycle/adapter_sessions/redaction.py +30 -0
  10. agent_lifecycle/adapter_sessions/session_store.py +64 -0
  11. agent_lifecycle/adapter_sessions/task_intake.py +450 -0
  12. agent_lifecycle/adapter_sessions/workflow_bridge.py +154 -0
  13. agent_lifecycle/audit/__init__.py +67 -0
  14. agent_lifecycle/audit/bug_forensics.py +94 -0
  15. agent_lifecycle/audit/implementation.py +516 -0
  16. agent_lifecycle/audit/ownership.py +119 -0
  17. agent_lifecycle/audit/proof_integrity.py +814 -0
  18. agent_lifecycle/audit/review_verdict.py +116 -0
  19. agent_lifecycle/changesets/__init__.py +5 -0
  20. agent_lifecycle/changesets/git.py +35 -0
  21. agent_lifecycle/cli/__init__.py +5 -0
  22. agent_lifecycle/cli/adapter.py +360 -0
  23. agent_lifecycle/cli/dispatch.py +1220 -0
  24. agent_lifecycle/cli/followup.py +89 -0
  25. agent_lifecycle/cli/main.py +41 -0
  26. agent_lifecycle/cli/metrics_parser.py +49 -0
  27. agent_lifecycle/cli/parsers.py +618 -0
  28. agent_lifecycle/cli/policy.py +101 -0
  29. agent_lifecycle/cli/progress_hooks.py +117 -0
  30. agent_lifecycle/cli/worktree.py +74 -0
  31. agent_lifecycle/compiler/__init__.py +17 -0
  32. agent_lifecycle/compiler/small_model_packets.py +322 -0
  33. agent_lifecycle/compiler/task_packets.py +229 -0
  34. agent_lifecycle/context/__init__.py +20 -0
  35. agent_lifecycle/context/episode_retrieval.py +30 -0
  36. agent_lifecycle/context/external_memory.py +71 -0
  37. agent_lifecycle/context/profiles.py +104 -0
  38. agent_lifecycle/context/rendering.py +226 -0
  39. agent_lifecycle/contracts/__init__.py +25 -0
  40. agent_lifecycle/contracts/adapter_contract_schemas.py +130 -0
  41. agent_lifecycle/contracts/adapter_event_schemas.py +118 -0
  42. agent_lifecycle/contracts/adapter_session_schemas.py +130 -0
  43. agent_lifecycle/contracts/adapter_task_schemas.py +90 -0
  44. agent_lifecycle/contracts/audit_schemas.py +133 -0
  45. agent_lifecycle/contracts/bug_forensics_schemas.py +400 -0
  46. agent_lifecycle/contracts/canonical.py +55 -0
  47. agent_lifecycle/contracts/compatibility.py +242 -0
  48. agent_lifecycle/contracts/context_model_schemas.py +265 -0
  49. agent_lifecycle/contracts/core_schemas.py +252 -0
  50. agent_lifecycle/contracts/cross_check_schemas.py +121 -0
  51. agent_lifecycle/contracts/errors.py +26 -0
  52. agent_lifecycle/contracts/evidence_import_schemas.py +152 -0
  53. agent_lifecycle/contracts/host_capability_schemas.py +64 -0
  54. agent_lifecycle/contracts/import_dialect_schemas.py +166 -0
  55. agent_lifecycle/contracts/metric_schemas.py +315 -0
  56. agent_lifecycle/contracts/paths.py +31 -0
  57. agent_lifecycle/contracts/plan_contract_schemas.py +148 -0
  58. agent_lifecycle/contracts/policy_schemas.py +350 -0
  59. agent_lifecycle/contracts/progress_bridge_schemas.py +114 -0
  60. agent_lifecycle/contracts/progress_hook_schemas.py +119 -0
  61. agent_lifecycle/contracts/proof_integrity_schemas.py +240 -0
  62. agent_lifecycle/contracts/release_contract_schemas.py +476 -0
  63. agent_lifecycle/contracts/review_mesh_recommendation_schemas.py +64 -0
  64. agent_lifecycle/contracts/review_mesh_schemas.py +222 -0
  65. agent_lifecycle/contracts/review_quality_schemas.py +109 -0
  66. agent_lifecycle/contracts/runner_schemas.py +91 -0
  67. agent_lifecycle/contracts/runner_worktree_schemas.py +389 -0
  68. agent_lifecycle/contracts/sandbox_schemas.py +159 -0
  69. agent_lifecycle/contracts/schema_builders.py +23 -0
  70. agent_lifecycle/contracts/schemas.py +93 -0
  71. agent_lifecycle/contracts/status_goal_schemas.py +469 -0
  72. agent_lifecycle/contracts/task_template_schemas.py +96 -0
  73. agent_lifecycle/contracts/usage_export_schemas.py +68 -0
  74. agent_lifecycle/diagnostics/__init__.py +17 -0
  75. agent_lifecycle/diagnostics/bundles.py +124 -0
  76. agent_lifecycle/diagnostics/readiness.py +733 -0
  77. agent_lifecycle/evidence_index/__init__.py +39 -0
  78. agent_lifecycle/evidence_index/core.py +272 -0
  79. agent_lifecycle/evidence_index/episode_index.py +329 -0
  80. agent_lifecycle/evidence_index/external_context.py +276 -0
  81. agent_lifecycle/followup/__init__.py +23 -0
  82. agent_lifecycle/followup/records.py +404 -0
  83. agent_lifecycle/freeze/__init__.py +5 -0
  84. agent_lifecycle/freeze/locks.py +23 -0
  85. agent_lifecycle/goal/__init__.py +15 -0
  86. agent_lifecycle/goal/records.py +244 -0
  87. agent_lifecycle/goal/view.py +109 -0
  88. agent_lifecycle/host_protocol/__init__.py +65 -0
  89. agent_lifecycle/host_protocol/acp_capability.py +195 -0
  90. agent_lifecycle/host_protocol/capabilities.py +268 -0
  91. agent_lifecycle/host_protocol/contracts.py +214 -0
  92. agent_lifecycle/host_protocol/event_capture.py +390 -0
  93. agent_lifecycle/host_protocol/events.py +196 -0
  94. agent_lifecycle/host_protocol/inspection.py +194 -0
  95. agent_lifecycle/host_protocol/inspection_common.py +310 -0
  96. agent_lifecycle/host_protocol/inspection_cursor.py +164 -0
  97. agent_lifecycle/host_protocol/inspection_gemini_cli.py +163 -0
  98. agent_lifecycle/host_protocol/inspection_hermes.py +161 -0
  99. agent_lifecycle/host_protocol/inspection_kimi_code.py +169 -0
  100. agent_lifecycle/host_protocol/inspection_opencode.py +147 -0
  101. agent_lifecycle/host_protocol/inspection_qwen_code.py +143 -0
  102. agent_lifecycle/host_protocol/receipts.py +58 -0
  103. agent_lifecycle/host_protocol/scaffold.py +329 -0
  104. agent_lifecycle/host_protocol/validation.py +229 -0
  105. agent_lifecycle/imports/__init__.py +76 -0
  106. agent_lifecycle/imports/agentskills_profile.py +54 -0
  107. agent_lifecycle/imports/bmad_profile.py +49 -0
  108. agent_lifecycle/imports/constitution_adr.py +91 -0
  109. agent_lifecycle/imports/dialect_profiles.py +121 -0
  110. agent_lifecycle/imports/external_agent.py +28 -0
  111. agent_lifecycle/imports/external_dialects.py +486 -0
  112. agent_lifecycle/imports/external_workflow.py +28 -0
  113. agent_lifecycle/imports/multi_markdown.py +111 -0
  114. agent_lifecycle/imports/openspec_profile.py +49 -0
  115. agent_lifecycle/imports/planning.py +536 -0
  116. agent_lifecycle/imports/spec_kit_profile.py +49 -0
  117. agent_lifecycle/imports/spec_kitty_profile.py +49 -0
  118. agent_lifecycle/metrics/__init__.py +60 -0
  119. agent_lifecycle/metrics/cost_collection.py +315 -0
  120. agent_lifecycle/metrics/costs.py +203 -0
  121. agent_lifecycle/metrics/outcome_index.py +330 -0
  122. agent_lifecycle/metrics/phase_resources.py +208 -0
  123. agent_lifecycle/metrics/recommendations.py +507 -0
  124. agent_lifecycle/metrics/regression_signals.py +64 -0
  125. agent_lifecycle/metrics/usage_export.py +390 -0
  126. agent_lifecycle/model_routing/__init__.py +19 -0
  127. agent_lifecycle/model_routing/profiles.py +323 -0
  128. agent_lifecycle/model_routing/receipts.py +140 -0
  129. agent_lifecycle/model_routing/resolver.py +566 -0
  130. agent_lifecycle/neutrality/__init__.py +12 -0
  131. agent_lifecycle/neutrality/__main__.py +6 -0
  132. agent_lifecycle/neutrality/authority.py +265 -0
  133. agent_lifecycle/neutrality/canonical.py +28 -0
  134. agent_lifecycle/neutrality/cli.py +294 -0
  135. agent_lifecycle/neutrality/ed25519.py +124 -0
  136. agent_lifecycle/neutrality/errors.py +89 -0
  137. agent_lifecycle/neutrality/gate.py +269 -0
  138. agent_lifecycle/neutrality/paths.py +92 -0
  139. agent_lifecycle/neutrality/policy.py +76 -0
  140. agent_lifecycle/neutrality/receipt.py +135 -0
  141. agent_lifecycle/neutrality/scanner.py +310 -0
  142. agent_lifecycle/neutrality/test_runner.py +116 -0
  143. agent_lifecycle/planning/__init__.py +47 -0
  144. agent_lifecycle/planning/acceptance_markdown.py +159 -0
  145. agent_lifecycle/planning/completeness.py +362 -0
  146. agent_lifecycle/planning/continuity.py +255 -0
  147. agent_lifecycle/planning/templates.py +294 -0
  148. agent_lifecycle/planning/tiering.py +87 -0
  149. agent_lifecycle/planning/validation.py +72 -0
  150. agent_lifecycle/policy/__init__.py +37 -0
  151. agent_lifecycle/policy/adaptive_lifecycle.py +416 -0
  152. agent_lifecycle/policy/apply.py +44 -0
  153. agent_lifecycle/policy/proposals.py +189 -0
  154. agent_lifecycle/policy/quality_floor.py +144 -0
  155. agent_lifecycle/policy/runtime_receipts.py +160 -0
  156. agent_lifecycle/py.typed +1 -0
  157. agent_lifecycle/quality/__init__.py +89 -0
  158. agent_lifecycle/quality/bug_forensics.py +727 -0
  159. agent_lifecycle/quality/bug_forensics_advisor.py +88 -0
  160. agent_lifecycle/quality/bug_forensics_recipes.py +239 -0
  161. agent_lifecycle/quality/cross_check.py +437 -0
  162. agent_lifecycle/quality/failure_classification.py +318 -0
  163. agent_lifecycle/quality/packs.py +265 -0
  164. agent_lifecycle/reporting/__init__.py +37 -0
  165. agent_lifecycle/reporting/change_summary.py +156 -0
  166. agent_lifecycle/reporting/event_feed.py +101 -0
  167. agent_lifecycle/reporting/progress_bridge.py +153 -0
  168. agent_lifecycle/reporting/progress_hooks.py +161 -0
  169. agent_lifecycle/reporting/progress_terminal.py +95 -0
  170. agent_lifecycle/reporting/progress_view.py +284 -0
  171. agent_lifecycle/reporting/status_view.py +125 -0
  172. agent_lifecycle/reporting/usage_export.py +59 -0
  173. agent_lifecycle/review/__init__.py +5 -0
  174. agent_lifecycle/review/validation.py +29 -0
  175. agent_lifecycle/review_mesh/__init__.py +83 -0
  176. agent_lifecycle/review_mesh/assignments.py +160 -0
  177. agent_lifecycle/review_mesh/contracts.py +685 -0
  178. agent_lifecycle/review_mesh/operator_templates.py +320 -0
  179. agent_lifecycle/review_mesh/quorum.py +38 -0
  180. agent_lifecycle/review_mesh/recommendation.py +487 -0
  181. agent_lifecycle/review_mesh/results.py +98 -0
  182. agent_lifecycle/review_mesh/synthesis.py +102 -0
  183. agent_lifecycle/runner/__init__.py +47 -0
  184. agent_lifecycle/runner/attempt_snapshots.py +244 -0
  185. agent_lifecycle/runner/core.py +653 -0
  186. agent_lifecycle/runner/sandbox_receipts.py +628 -0
  187. agent_lifecycle/specification/__init__.py +23 -0
  188. agent_lifecycle/specification/completion_check.py +135 -0
  189. agent_lifecycle/specification/completion_gate.py +501 -0
  190. agent_lifecycle/specification/completion_signal.py +75 -0
  191. agent_lifecycle/specification/validation.py +48 -0
  192. agent_lifecycle/workflow/__init__.py +79 -0
  193. agent_lifecycle/workflow/artifacts.py +74 -0
  194. agent_lifecycle/workflow/budget_decisions.py +370 -0
  195. agent_lifecycle/workflow/budget_policy.py +140 -0
  196. agent_lifecycle/workflow/budget_receipts.py +70 -0
  197. agent_lifecycle/workflow/bug_forensics_gates.py +284 -0
  198. agent_lifecycle/workflow/controller.py +45 -0
  199. agent_lifecycle/workflow/events.py +50 -0
  200. agent_lifecycle/workflow/final_proof_integrity.py +74 -0
  201. agent_lifecycle/workflow/finalization.py +422 -0
  202. agent_lifecycle/workflow/gates.py +185 -0
  203. agent_lifecycle/workflow/implementation_audit_gate.py +126 -0
  204. agent_lifecycle/workflow/leases.py +220 -0
  205. agent_lifecycle/workflow/lineage.py +108 -0
  206. agent_lifecycle/workflow/managed_runner.py +167 -0
  207. agent_lifecycle/workflow/model_usage.py +106 -0
  208. agent_lifecycle/workflow/next_action.py +86 -0
  209. agent_lifecycle/workflow/operation_kernel.py +98 -0
  210. agent_lifecycle/workflow/plan_adoption.py +439 -0
  211. agent_lifecycle/workflow/query.py +52 -0
  212. agent_lifecycle/workflow/review_mesh_gate.py +93 -0
  213. agent_lifecycle/workflow/reviews.py +168 -0
  214. agent_lifecycle/workflow/run_transitions.py +163 -0
  215. agent_lifecycle/workflow/sandbox_policy.py +214 -0
  216. agent_lifecycle/workflow/selectors.py +57 -0
  217. agent_lifecycle/workflow/state.py +130 -0
  218. agent_lifecycle/workflow/task_transitions.py +421 -0
  219. agent_lifecycle/worktree/__init__.py +17 -0
  220. agent_lifecycle/worktree/isolation.py +407 -0
  221. agent_lifecycle_kit-1.50.1.dist-info/METADATA +201 -0
  222. agent_lifecycle_kit-1.50.1.dist-info/RECORD +227 -0
  223. agent_lifecycle_kit-1.50.1.dist-info/WHEEL +5 -0
  224. agent_lifecycle_kit-1.50.1.dist-info/entry_points.txt +3 -0
  225. agent_lifecycle_kit-1.50.1.dist-info/licenses/LICENSE +201 -0
  226. agent_lifecycle_kit-1.50.1.dist-info/licenses/NOTICE +5 -0
  227. agent_lifecycle_kit-1.50.1.dist-info/top_level.txt +1 -0
@@ -0,0 +1,163 @@
1
+ """Safe host inspection probes for Gemini CLI."""
2
+
3
+ from __future__ import annotations
4
+
5
+ from pathlib import Path
6
+ from typing import Any
7
+
8
+ from agent_lifecycle.host_protocol.inspection_common import (
9
+ CommandRunner,
10
+ _check_cursor_plugin_config,
11
+ _check_hermes_registry,
12
+ _check_hermes_skills_config,
13
+ _check_hermes_slash_commands,
14
+ _check_json_plugin_config,
15
+ _check_scaffold_projection_files,
16
+ _cursor_model_count,
17
+ _cursor_subscription_tier,
18
+ _display_binary,
19
+ _first_non_empty_line,
20
+ _missing_markers,
21
+ _run_command_check,
22
+ _run_command_check_with_text,
23
+ )
24
+
25
+ def _inspect_gemini_cli(
26
+ *,
27
+ descriptor_path: Path | None,
28
+ host_bin: str,
29
+ project_root: Path,
30
+ timeout_seconds: float,
31
+ command_runner: CommandRunner,
32
+ ) -> tuple[list[dict[str, Any]], dict[str, Any], list[dict[str, Any]]]:
33
+ checks: list[dict[str, Any]] = []
34
+ blockers: list[dict[str, Any]] = []
35
+ capabilities: dict[str, Any] = {
36
+ "hostCommands": {
37
+ "status": "CHECKED",
38
+ "binary": _display_binary(host_bin),
39
+ "profile": "gemini-cli-safe-cli",
40
+ },
41
+ "headlessExecution": {"status": "UNKNOWN"},
42
+ "eventStream": {"status": "UNKNOWN"},
43
+ "usageAttestation": {"status": "UNKNOWN", "requiresLiveReceipt": True},
44
+ "permissionMode": {"status": "UNKNOWN"},
45
+ "modelSelection": {"status": "UNKNOWN"},
46
+ "skillDiscovery": {"status": "UNKNOWN"},
47
+ "extensionDiscovery": {"status": "UNKNOWN"},
48
+ "mcpDiscovery": {"status": "UNKNOWN"},
49
+ "localModelRouting": {"status": "UNKNOWN"},
50
+ "authState": {"status": "NOT_PROBED", "reason": "no-safe-auth-status-command-discovered"},
51
+ "configuration": {},
52
+ }
53
+
54
+ if descriptor_path is not None:
55
+ projection = _check_scaffold_projection_files(descriptor_path.parent, project_root=project_root, host="gemini-cli")
56
+ checks.append(projection)
57
+ if projection["status"] == "FAIL":
58
+ blockers.append({"code": "gemini-cli-projection-unavailable", "message": "Gemini CLI scaffold projection files are missing or invalid"})
59
+ capabilities["configuration"]["projection"] = projection["details"]
60
+
61
+ version_check, version_text = _run_command_check(
62
+ "gemini-cli-version",
63
+ [host_bin, "--version"],
64
+ timeout_seconds=timeout_seconds,
65
+ command_runner=command_runner,
66
+ )
67
+ checks.append(version_check)
68
+ if version_check["status"] == "FAIL":
69
+ blockers.append({"code": "gemini-cli-binary-unavailable", "message": "Gemini CLI binary is unavailable or failed version probing"})
70
+ capabilities["hostVersion"] = version_text
71
+
72
+ help_check, _ = _run_command_check(
73
+ "gemini-cli-help",
74
+ [host_bin, "--help"],
75
+ timeout_seconds=timeout_seconds,
76
+ command_runner=command_runner,
77
+ required_markers=[
78
+ "--prompt",
79
+ "--output-format",
80
+ "stream-json",
81
+ "--model",
82
+ "--yolo",
83
+ "--approval-mode",
84
+ "--sandbox",
85
+ "--worktree",
86
+ "skills",
87
+ "mcp",
88
+ "extensions",
89
+ ],
90
+ )
91
+ checks.append(help_check)
92
+ if help_check["status"] == "FAIL":
93
+ blockers.append({"code": "gemini-cli-help-unavailable", "message": "Gemini CLI help does not expose required adapter surfaces"})
94
+ else:
95
+ capabilities["headlessExecution"] = {"status": "SUPPORTED", "command": "--prompt", "streamJson": "--output-format stream-json"}
96
+ capabilities["eventStream"] = {"status": "DISCOVERED", "source": "stream-json-output", "requiresReceiptValidation": True}
97
+ capabilities["usageAttestation"] = {
98
+ "status": "UNPROVEN",
99
+ "source": "stream-json-output",
100
+ "requiresLiveReceipt": True,
101
+ }
102
+ capabilities["permissionMode"] = {
103
+ "status": "DISCOVERED",
104
+ "yoloFlag": "--yolo",
105
+ "approvalMode": "--approval-mode",
106
+ "sandboxFlag": "--sandbox",
107
+ "adapterPolicy": "fail-closed",
108
+ }
109
+ capabilities["modelSelection"] = {"status": "DISCOVERED", "selector": "--model"}
110
+
111
+ skills_help, _ = _run_command_check(
112
+ "gemini-cli-skills-help",
113
+ [host_bin, "skills", "--help"],
114
+ timeout_seconds=timeout_seconds,
115
+ command_runner=command_runner,
116
+ required_markers=["list", "install", "enable", "disable"],
117
+ )
118
+ checks.append(skills_help)
119
+ if skills_help["status"] == "FAIL":
120
+ blockers.append({"code": "gemini-cli-skills-surface-unavailable", "message": "Gemini CLI skills command surface is unavailable"})
121
+ else:
122
+ capabilities["skillDiscovery"] = {"status": "DISCOVERED", "source": "skills-command"}
123
+
124
+ extensions_help, _ = _run_command_check(
125
+ "gemini-cli-extensions-help",
126
+ [host_bin, "extensions", "--help"],
127
+ timeout_seconds=timeout_seconds,
128
+ command_runner=command_runner,
129
+ required_markers=["install", "validate", "list"],
130
+ )
131
+ checks.append(extensions_help)
132
+ if extensions_help["status"] == "FAIL":
133
+ blockers.append({"code": "gemini-cli-extensions-surface-unavailable", "message": "Gemini CLI extensions command surface is unavailable"})
134
+ else:
135
+ capabilities["extensionDiscovery"] = {"status": "DISCOVERED", "source": "extensions-command"}
136
+
137
+ mcp_help, _ = _run_command_check(
138
+ "gemini-cli-mcp-help",
139
+ [host_bin, "mcp", "--help"],
140
+ timeout_seconds=timeout_seconds,
141
+ command_runner=command_runner,
142
+ required_markers=["add", "list", "enable", "disable"],
143
+ )
144
+ checks.append(mcp_help)
145
+ if mcp_help["status"] == "FAIL":
146
+ blockers.append({"code": "gemini-cli-mcp-surface-unavailable", "message": "Gemini CLI MCP command surface is unavailable"})
147
+ else:
148
+ capabilities["mcpDiscovery"] = {"status": "DISCOVERED", "source": "mcp-command"}
149
+
150
+ gemma_help, _ = _run_command_check(
151
+ "gemini-cli-gemma-help",
152
+ [host_bin, "gemma", "--help"],
153
+ timeout_seconds=timeout_seconds,
154
+ command_runner=command_runner,
155
+ required_markers=["setup", "start", "status"],
156
+ )
157
+ checks.append(gemma_help)
158
+ if gemma_help["status"] == "FAIL":
159
+ blockers.append({"code": "gemini-cli-local-model-surface-unavailable", "message": "Gemini CLI local model routing surface is unavailable"})
160
+ else:
161
+ capabilities["localModelRouting"] = {"status": "DISCOVERED", "source": "gemma-command"}
162
+
163
+ return checks, capabilities, blockers
@@ -0,0 +1,161 @@
1
+ """Safe host inspection probes for Hermes."""
2
+
3
+ from __future__ import annotations
4
+
5
+ from pathlib import Path
6
+ from typing import Any
7
+
8
+ from agent_lifecycle.host_protocol.inspection_common import (
9
+ CommandRunner,
10
+ _check_cursor_plugin_config,
11
+ _check_hermes_registry,
12
+ _check_hermes_skills_config,
13
+ _check_hermes_slash_commands,
14
+ _check_json_plugin_config,
15
+ _check_scaffold_projection_files,
16
+ _cursor_model_count,
17
+ _cursor_subscription_tier,
18
+ _display_binary,
19
+ _first_non_empty_line,
20
+ _missing_markers,
21
+ _run_command_check,
22
+ _run_command_check_with_text,
23
+ )
24
+
25
+ def _inspect_hermes(
26
+ *,
27
+ descriptor_maturity: Any,
28
+ descriptor_path: Path | None,
29
+ host_bin: str,
30
+ project_root: Path,
31
+ timeout_seconds: float,
32
+ command_runner: CommandRunner,
33
+ ) -> tuple[list[dict[str, Any]], dict[str, Any], list[dict[str, Any]]]:
34
+ checks: list[dict[str, Any]] = []
35
+ blockers: list[dict[str, Any]] = []
36
+ capabilities: dict[str, Any] = {
37
+ "hostCommands": {
38
+ "status": "CHECKED",
39
+ "binary": _display_binary(host_bin),
40
+ "profile": "hermes-safe-cli",
41
+ },
42
+ "headlessExecution": {"status": "UNKNOWN"},
43
+ "eventStream": {"status": "UNKNOWN"},
44
+ "usageAttestation": {"status": "UNKNOWN", "requiresLiveReceipt": True},
45
+ "permissionMode": {"status": "UNKNOWN"},
46
+ "modelSelection": {"status": "UNKNOWN"},
47
+ "skillDiscovery": {"status": "UNKNOWN"},
48
+ "slashCommands": {"status": "UNKNOWN"},
49
+ "authState": {"status": "NOT_PROBED", "reason": "credential-state-redacted"},
50
+ "configuration": {},
51
+ }
52
+
53
+ root_skills = _check_hermes_skills_config(project_root / "skills.sh.json", project_root=project_root)
54
+ checks.append(root_skills)
55
+ if root_skills["status"] == "FAIL":
56
+ blockers.append({"code": "hermes-skills-config-unavailable", "message": "Hermes skills config is missing or invalid"})
57
+ capabilities["configuration"]["skillsConfig"] = root_skills["details"]
58
+
59
+ if descriptor_path is not None:
60
+ registry = _check_hermes_registry(
61
+ descriptor_path.parent / "hermes.registry.json",
62
+ project_root=project_root,
63
+ expected_maturity=str(descriptor_maturity) if isinstance(descriptor_maturity, str) else None,
64
+ )
65
+ checks.append(registry)
66
+ if registry["status"] == "FAIL":
67
+ blockers.append({"code": "hermes-registry-unavailable", "message": "Hermes registry metadata is missing or invalid"})
68
+ capabilities["configuration"]["registry"] = registry["details"]
69
+
70
+ slash_commands = _check_hermes_slash_commands(descriptor_path.parent / "slash-commands.json", project_root=project_root)
71
+ checks.append(slash_commands)
72
+ if slash_commands["status"] == "FAIL":
73
+ blockers.append({"code": "hermes-slash-commands-unavailable", "message": "Hermes slash-command metadata is missing or invalid"})
74
+ capabilities["configuration"]["slashCommands"] = slash_commands["details"]
75
+ if slash_commands["status"] == "PASS":
76
+ capabilities["slashCommands"] = {"status": "DISCOVERED", "unsupportedOperationPolicy": "fail-closed"}
77
+
78
+ version_check, version_text = _run_command_check(
79
+ "hermes-version",
80
+ [host_bin, "--version"],
81
+ timeout_seconds=timeout_seconds,
82
+ command_runner=command_runner,
83
+ )
84
+ checks.append(version_check)
85
+ if version_check["status"] == "FAIL":
86
+ blockers.append({"code": "hermes-binary-unavailable", "message": "Hermes binary is unavailable or failed version probing"})
87
+ capabilities["hostVersion"] = version_text
88
+
89
+ root_help, _ = _run_command_check(
90
+ "hermes-root-help",
91
+ [host_bin, "--help"],
92
+ timeout_seconds=timeout_seconds,
93
+ command_runner=command_runner,
94
+ required_markers=["--oneshot", "--usage-file", "--model", "--provider", "--yolo", "--safe-mode", "skills", "auth", "status"],
95
+ )
96
+ checks.append(root_help)
97
+ if root_help["status"] == "FAIL":
98
+ blockers.append({"code": "hermes-root-help-unavailable", "message": "Hermes root help does not expose required adapter surfaces"})
99
+ else:
100
+ capabilities["headlessExecution"] = {"status": "DISCOVERED", "command": "--oneshot", "usageFile": "--usage-file"}
101
+ capabilities["usageAttestation"] = {
102
+ "status": "DISCOVERED",
103
+ "source": "usage-file",
104
+ "requiresLiveReceipt": True,
105
+ }
106
+ capabilities["permissionMode"] = {"status": "DISCOVERED", "autoApproveFlag": "--yolo", "safeModeFlag": "--safe-mode", "adapterPolicy": "fail-closed"}
107
+ capabilities["modelSelection"] = {"status": "DISCOVERED", "selector": "--model", "providerSelector": "--provider"}
108
+
109
+ chat_help, _ = _run_command_check(
110
+ "hermes-chat-help",
111
+ [host_bin, "chat", "--help"],
112
+ timeout_seconds=timeout_seconds,
113
+ command_runner=command_runner,
114
+ required_markers=["--query", "--model", "--provider", "--toolsets", "--skills", "--yolo", "--safe-mode"],
115
+ )
116
+ checks.append(chat_help)
117
+ if chat_help["status"] == "FAIL":
118
+ blockers.append({"code": "hermes-chat-help-unavailable", "message": "Hermes chat help does not expose required adapter surfaces"})
119
+
120
+ skills_help, _ = _run_command_check(
121
+ "hermes-skills-help",
122
+ [host_bin, "skills", "--help"],
123
+ timeout_seconds=timeout_seconds,
124
+ command_runner=command_runner,
125
+ required_markers=["install", "list", "check"],
126
+ )
127
+ checks.append(skills_help)
128
+ if skills_help["status"] == "FAIL":
129
+ blockers.append({"code": "hermes-skills-help-unavailable", "message": "Hermes skills help does not expose required adapter surfaces"})
130
+ else:
131
+ capabilities["skillDiscovery"] = {"status": "DISCOVERED", "source": "skills-command"}
132
+
133
+ auth_help, _ = _run_command_check(
134
+ "hermes-auth-help",
135
+ [host_bin, "auth", "--help"],
136
+ timeout_seconds=timeout_seconds,
137
+ command_runner=command_runner,
138
+ required_markers=["status", "list", "logout"],
139
+ )
140
+ checks.append(auth_help)
141
+ if auth_help["status"] == "FAIL":
142
+ blockers.append({"code": "hermes-auth-surface-unavailable", "message": "Hermes auth command surface is unavailable"})
143
+ else:
144
+ capabilities["authState"] = {
145
+ "status": "NOT_DISCLOSED",
146
+ "commandSurface": "DISCOVERED",
147
+ "reason": "credential-state-redacted",
148
+ }
149
+
150
+ status_help, _ = _run_command_check(
151
+ "hermes-status-help",
152
+ [host_bin, "status", "--help"],
153
+ timeout_seconds=timeout_seconds,
154
+ command_runner=command_runner,
155
+ required_markers=["--all", "redacted"],
156
+ )
157
+ checks.append(status_help)
158
+ if status_help["status"] == "FAIL":
159
+ blockers.append({"code": "hermes-status-surface-unavailable", "message": "Hermes status command surface is unavailable"})
160
+
161
+ return checks, capabilities, blockers
@@ -0,0 +1,169 @@
1
+ """Safe host inspection probes for Kimi Code."""
2
+
3
+ from __future__ import annotations
4
+
5
+ from pathlib import Path
6
+ from typing import Any
7
+
8
+ from agent_lifecycle.host_protocol.inspection_common import (
9
+ CommandRunner,
10
+ _check_cursor_plugin_config,
11
+ _check_hermes_registry,
12
+ _check_hermes_skills_config,
13
+ _check_hermes_slash_commands,
14
+ _check_json_plugin_config,
15
+ _check_scaffold_projection_files,
16
+ _cursor_model_count,
17
+ _cursor_subscription_tier,
18
+ _display_binary,
19
+ _first_non_empty_line,
20
+ _missing_markers,
21
+ _run_command_check,
22
+ _run_command_check_with_text,
23
+ )
24
+
25
+ def _inspect_kimi_code(
26
+ *,
27
+ descriptor_path: Path | None,
28
+ host_bin: str,
29
+ project_root: Path,
30
+ timeout_seconds: float,
31
+ command_runner: CommandRunner,
32
+ ) -> tuple[list[dict[str, Any]], dict[str, Any], list[dict[str, Any]]]:
33
+ checks: list[dict[str, Any]] = []
34
+ blockers: list[dict[str, Any]] = []
35
+ capabilities: dict[str, Any] = {
36
+ "hostCommands": {
37
+ "status": "CHECKED",
38
+ "binary": _display_binary(host_bin),
39
+ "profile": "kimi-code-safe-cli",
40
+ },
41
+ "headlessExecution": {"status": "UNKNOWN"},
42
+ "eventStream": {"status": "UNKNOWN"},
43
+ "usageAttestation": {"status": "UNKNOWN", "requiresLiveReceipt": True},
44
+ "permissionMode": {"status": "UNKNOWN"},
45
+ "modelSelection": {"status": "UNKNOWN"},
46
+ "skillDiscovery": {"status": "UNKNOWN"},
47
+ "providerDiscovery": {"status": "UNKNOWN"},
48
+ "agentProtocol": {"status": "UNKNOWN"},
49
+ "resultExport": {"status": "UNKNOWN"},
50
+ "configurationValidation": {"status": "UNKNOWN"},
51
+ "authState": {"status": "NOT_PROBED", "reason": "credential-state-redacted"},
52
+ "configuration": {},
53
+ }
54
+
55
+ if descriptor_path is not None:
56
+ projection = _check_scaffold_projection_files(descriptor_path.parent, project_root=project_root, host="kimi-code")
57
+ checks.append(projection)
58
+ if projection["status"] == "FAIL":
59
+ blockers.append({"code": "kimi-code-projection-unavailable", "message": "Kimi Code scaffold projection files are missing or invalid"})
60
+ capabilities["configuration"]["projection"] = projection["details"]
61
+
62
+ version_check, version_text = _run_command_check(
63
+ "kimi-code-version",
64
+ [host_bin, "--version"],
65
+ timeout_seconds=timeout_seconds,
66
+ command_runner=command_runner,
67
+ )
68
+ checks.append(version_check)
69
+ if version_check["status"] == "FAIL":
70
+ blockers.append({"code": "kimi-code-binary-unavailable", "message": "Kimi Code binary is unavailable or failed version probing"})
71
+ capabilities["hostCommands"]["status"] = "BINARY_MISSING"
72
+ capabilities["hostVersion"] = None
73
+ return checks, capabilities, blockers
74
+ capabilities["hostVersion"] = version_text
75
+
76
+ help_check, _ = _run_command_check(
77
+ "kimi-code-help",
78
+ [host_bin, "--help"],
79
+ timeout_seconds=timeout_seconds,
80
+ command_runner=command_runner,
81
+ required_markers=[
82
+ "--prompt",
83
+ "--output-format",
84
+ "stream-json",
85
+ "--model",
86
+ "--yolo",
87
+ "--auto",
88
+ "--skills-dir",
89
+ "--session",
90
+ "--continue",
91
+ "--plan",
92
+ "export",
93
+ "provider",
94
+ "acp",
95
+ "login",
96
+ "doctor",
97
+ ],
98
+ )
99
+ checks.append(help_check)
100
+ if help_check["status"] == "FAIL":
101
+ blockers.append({"code": "kimi-code-help-unavailable", "message": "Kimi Code help surface is unavailable"})
102
+ else:
103
+ capabilities["headlessExecution"] = {"status": "SUPPORTED", "command": "--prompt", "streamJson": "--output-format stream-json"}
104
+ capabilities["eventStream"] = {"status": "DISCOVERED", "source": "stream-json-output", "requiresReceiptValidation": True}
105
+ capabilities["usageAttestation"] = {"status": "UNPROVEN", "requiresLiveReceipt": True}
106
+ capabilities["permissionMode"] = {
107
+ "status": "DISCOVERED",
108
+ "yoloFlag": "--yolo",
109
+ "autoFlag": "--auto",
110
+ "planFlag": "--plan",
111
+ "adapterPolicy": "fail-closed",
112
+ }
113
+ capabilities["modelSelection"] = {"status": "DISCOVERED", "selector": "--model"}
114
+ capabilities["skillDiscovery"] = {"status": "DISCOVERED", "source": "skills-dir"}
115
+ capabilities["authState"] = {"status": "NOT_PROBED", "commandSurface": "DISCOVERED", "reason": "login-is-device-code-flow"}
116
+
117
+ provider_help, _ = _run_command_check(
118
+ "kimi-code-provider-help",
119
+ [host_bin, "provider", "--help"],
120
+ timeout_seconds=timeout_seconds,
121
+ command_runner=command_runner,
122
+ required_markers=["add", "remove", "list", "catalog"],
123
+ )
124
+ checks.append(provider_help)
125
+ if provider_help["status"] == "FAIL":
126
+ blockers.append({"code": "kimi-code-provider-surface-unavailable", "message": "Kimi Code provider command surface is unavailable"})
127
+ else:
128
+ capabilities["providerDiscovery"] = {"status": "DISCOVERED", "source": "provider-command"}
129
+
130
+ export_help, _ = _run_command_check(
131
+ "kimi-code-export-help",
132
+ [host_bin, "export", "--help"],
133
+ timeout_seconds=timeout_seconds,
134
+ command_runner=command_runner,
135
+ required_markers=["sessionId", "--output", "--yes"],
136
+ )
137
+ checks.append(export_help)
138
+ if export_help["status"] == "FAIL":
139
+ blockers.append({"code": "kimi-code-export-surface-unavailable", "message": "Kimi Code export command surface is unavailable"})
140
+ else:
141
+ capabilities["resultExport"] = {"status": "DISCOVERED", "command": "export"}
142
+
143
+ acp_help, _ = _run_command_check(
144
+ "kimi-code-acp-help",
145
+ [host_bin, "acp", "--help"],
146
+ timeout_seconds=timeout_seconds,
147
+ command_runner=command_runner,
148
+ required_markers=["Agent Client Protocol", "stdio", "--login"],
149
+ )
150
+ checks.append(acp_help)
151
+ if acp_help["status"] == "FAIL":
152
+ blockers.append({"code": "kimi-code-acp-surface-unavailable", "message": "Kimi Code ACP command surface is unavailable"})
153
+ else:
154
+ capabilities["agentProtocol"] = {"status": "DISCOVERED", "source": "acp-stdio-server"}
155
+
156
+ doctor_help, _ = _run_command_check(
157
+ "kimi-code-doctor-help",
158
+ [host_bin, "doctor", "--help"],
159
+ timeout_seconds=timeout_seconds,
160
+ command_runner=command_runner,
161
+ required_markers=["config", "tui"],
162
+ )
163
+ checks.append(doctor_help)
164
+ if doctor_help["status"] == "FAIL":
165
+ blockers.append({"code": "kimi-code-doctor-surface-unavailable", "message": "Kimi Code configuration validation surface is unavailable"})
166
+ else:
167
+ capabilities["configurationValidation"] = {"status": "DISCOVERED", "source": "doctor-command"}
168
+
169
+ return checks, capabilities, blockers
@@ -0,0 +1,147 @@
1
+ """Safe host inspection probes for OpenCode."""
2
+
3
+ from __future__ import annotations
4
+
5
+ from pathlib import Path
6
+ from typing import Any
7
+
8
+ from agent_lifecycle.host_protocol.inspection_common import (
9
+ CommandRunner,
10
+ _check_cursor_plugin_config,
11
+ _check_hermes_registry,
12
+ _check_hermes_skills_config,
13
+ _check_hermes_slash_commands,
14
+ _check_json_plugin_config,
15
+ _check_scaffold_projection_files,
16
+ _cursor_model_count,
17
+ _cursor_subscription_tier,
18
+ _display_binary,
19
+ _first_non_empty_line,
20
+ _missing_markers,
21
+ _run_command_check,
22
+ _run_command_check_with_text,
23
+ )
24
+
25
+ def _inspect_opencode(
26
+ *,
27
+ descriptor_path: Path | None,
28
+ host_bin: str,
29
+ project_root: Path,
30
+ timeout_seconds: float,
31
+ command_runner: CommandRunner,
32
+ ) -> tuple[list[dict[str, Any]], dict[str, Any], list[dict[str, Any]]]:
33
+ checks: list[dict[str, Any]] = []
34
+ blockers: list[dict[str, Any]] = []
35
+ capabilities: dict[str, Any] = {
36
+ "hostCommands": {
37
+ "status": "CHECKED",
38
+ "binary": _display_binary(host_bin),
39
+ "profile": "opencode-safe-cli",
40
+ },
41
+ "headlessExecution": {"status": "UNKNOWN"},
42
+ "eventStream": {"status": "UNKNOWN"},
43
+ "usageAttestation": {"status": "UNKNOWN", "requiresLiveReceipt": True},
44
+ "permissionMode": {"status": "UNKNOWN"},
45
+ "modelSelection": {"status": "UNKNOWN"},
46
+ "resultExport": {"status": "UNKNOWN"},
47
+ "authState": {"status": "NOT_PROBED", "reason": "credential-state-redacted"},
48
+ "configuration": {},
49
+ }
50
+
51
+ root_config = _check_json_plugin_config(
52
+ project_root / "opencode.json",
53
+ project_root=project_root,
54
+ expected_plugin="./adapters/opencode/plugins/agent-lifecycle-kit.js",
55
+ check_name="opencode-root-config",
56
+ )
57
+ checks.append(root_config)
58
+ if root_config["status"] == "FAIL":
59
+ blockers.append({"code": "opencode-root-config-unavailable", "message": "OpenCode root plugin config is missing or invalid"})
60
+ capabilities["configuration"]["rootConfig"] = root_config["details"]
61
+
62
+ if descriptor_path is not None:
63
+ adapter_config_path = descriptor_path.parent / "opencode.json"
64
+ adapter_config = _check_json_plugin_config(
65
+ adapter_config_path,
66
+ project_root=project_root,
67
+ expected_plugin="./plugins/agent-lifecycle-kit.js",
68
+ check_name="opencode-adapter-config",
69
+ )
70
+ checks.append(adapter_config)
71
+ if adapter_config["status"] == "FAIL":
72
+ blockers.append({"code": "opencode-adapter-config-unavailable", "message": "OpenCode adapter plugin config is missing or invalid"})
73
+ capabilities["configuration"]["adapterConfig"] = adapter_config["details"]
74
+
75
+ version_check, version_text = _run_command_check(
76
+ "opencode-version",
77
+ [host_bin, "--version"],
78
+ timeout_seconds=timeout_seconds,
79
+ command_runner=command_runner,
80
+ )
81
+ checks.append(version_check)
82
+ if version_check["status"] == "FAIL":
83
+ blockers.append({"code": "opencode-binary-unavailable", "message": "OpenCode binary is unavailable or failed version probing"})
84
+ capabilities["hostVersion"] = version_text
85
+
86
+ auth_help, _ = _run_command_check(
87
+ "opencode-auth-help",
88
+ [host_bin, "auth", "--help"],
89
+ timeout_seconds=timeout_seconds,
90
+ command_runner=command_runner,
91
+ required_markers=["auth", "login", "logout"],
92
+ )
93
+ checks.append(auth_help)
94
+ if auth_help["status"] == "FAIL":
95
+ blockers.append({"code": "opencode-auth-surface-unavailable", "message": "OpenCode auth command surface is unavailable"})
96
+ else:
97
+ capabilities["authState"] = {
98
+ "status": "NOT_DISCLOSED",
99
+ "commandSurface": "DISCOVERED",
100
+ "reason": "credential-state-redacted",
101
+ }
102
+
103
+ run_help, _ = _run_command_check(
104
+ "opencode-run-help",
105
+ [host_bin, "run", "--help"],
106
+ timeout_seconds=timeout_seconds,
107
+ command_runner=command_runner,
108
+ required_markers=["--format", "json", "--dir", "--auto", "--model"],
109
+ )
110
+ checks.append(run_help)
111
+ if run_help["status"] == "FAIL":
112
+ blockers.append({"code": "opencode-headless-run-unavailable", "message": "OpenCode run help does not expose required headless adapter flags"})
113
+ else:
114
+ capabilities["headlessExecution"] = {"status": "SUPPORTED", "command": "run", "jsonFormat": "--format json", "workingDirectory": "--dir"}
115
+ capabilities["eventStream"] = {"status": "DISCOVERED", "source": "run-json-output", "requiresReceiptValidation": True}
116
+ capabilities["permissionMode"] = {"status": "DISCOVERED", "autoApproveFlag": "--auto", "adapterPolicy": "fail-closed"}
117
+ capabilities["modelSelection"] = {"status": "DISCOVERED", "selector": "--model"}
118
+
119
+ export_help, _ = _run_command_check(
120
+ "opencode-export-help",
121
+ [host_bin, "export", "--help"],
122
+ timeout_seconds=timeout_seconds,
123
+ command_runner=command_runner,
124
+ )
125
+ checks.append(export_help)
126
+ if export_help["status"] == "FAIL":
127
+ blockers.append({"code": "opencode-export-unavailable", "message": "OpenCode export command is unavailable"})
128
+ else:
129
+ capabilities["resultExport"] = {"status": "DISCOVERED", "command": "export"}
130
+
131
+ stats_help, _ = _run_command_check(
132
+ "opencode-stats-help",
133
+ [host_bin, "stats", "--help"],
134
+ timeout_seconds=timeout_seconds,
135
+ command_runner=command_runner,
136
+ )
137
+ checks.append(stats_help)
138
+ if stats_help["status"] == "FAIL":
139
+ blockers.append({"code": "opencode-stats-unavailable", "message": "OpenCode stats command is unavailable"})
140
+ else:
141
+ capabilities["usageAttestation"] = {
142
+ "status": "DISCOVERED",
143
+ "source": "stats-and-run-json-output",
144
+ "requiresLiveReceipt": True,
145
+ }
146
+
147
+ return checks, capabilities, blockers