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.
- agent_lifecycle/__init__.py +5 -0
- agent_lifecycle/__main__.py +5 -0
- agent_lifecycle/_version.py +3 -0
- agent_lifecycle/adapter_sessions/__init__.py +41 -0
- agent_lifecycle/adapter_sessions/contracts.py +133 -0
- agent_lifecycle/adapter_sessions/env.py +44 -0
- agent_lifecycle/adapter_sessions/launcher.py +101 -0
- agent_lifecycle/adapter_sessions/process.py +39 -0
- agent_lifecycle/adapter_sessions/redaction.py +30 -0
- agent_lifecycle/adapter_sessions/session_store.py +64 -0
- agent_lifecycle/adapter_sessions/task_intake.py +450 -0
- agent_lifecycle/adapter_sessions/workflow_bridge.py +154 -0
- agent_lifecycle/audit/__init__.py +67 -0
- agent_lifecycle/audit/bug_forensics.py +94 -0
- agent_lifecycle/audit/implementation.py +516 -0
- agent_lifecycle/audit/ownership.py +119 -0
- agent_lifecycle/audit/proof_integrity.py +814 -0
- agent_lifecycle/audit/review_verdict.py +116 -0
- agent_lifecycle/changesets/__init__.py +5 -0
- agent_lifecycle/changesets/git.py +35 -0
- agent_lifecycle/cli/__init__.py +5 -0
- agent_lifecycle/cli/adapter.py +360 -0
- agent_lifecycle/cli/dispatch.py +1220 -0
- agent_lifecycle/cli/followup.py +89 -0
- agent_lifecycle/cli/main.py +41 -0
- agent_lifecycle/cli/metrics_parser.py +49 -0
- agent_lifecycle/cli/parsers.py +618 -0
- agent_lifecycle/cli/policy.py +101 -0
- agent_lifecycle/cli/progress_hooks.py +117 -0
- agent_lifecycle/cli/worktree.py +74 -0
- agent_lifecycle/compiler/__init__.py +17 -0
- agent_lifecycle/compiler/small_model_packets.py +322 -0
- agent_lifecycle/compiler/task_packets.py +229 -0
- agent_lifecycle/context/__init__.py +20 -0
- agent_lifecycle/context/episode_retrieval.py +30 -0
- agent_lifecycle/context/external_memory.py +71 -0
- agent_lifecycle/context/profiles.py +104 -0
- agent_lifecycle/context/rendering.py +226 -0
- agent_lifecycle/contracts/__init__.py +25 -0
- agent_lifecycle/contracts/adapter_contract_schemas.py +130 -0
- agent_lifecycle/contracts/adapter_event_schemas.py +118 -0
- agent_lifecycle/contracts/adapter_session_schemas.py +130 -0
- agent_lifecycle/contracts/adapter_task_schemas.py +90 -0
- agent_lifecycle/contracts/audit_schemas.py +133 -0
- agent_lifecycle/contracts/bug_forensics_schemas.py +400 -0
- agent_lifecycle/contracts/canonical.py +55 -0
- agent_lifecycle/contracts/compatibility.py +242 -0
- agent_lifecycle/contracts/context_model_schemas.py +265 -0
- agent_lifecycle/contracts/core_schemas.py +252 -0
- agent_lifecycle/contracts/cross_check_schemas.py +121 -0
- agent_lifecycle/contracts/errors.py +26 -0
- agent_lifecycle/contracts/evidence_import_schemas.py +152 -0
- agent_lifecycle/contracts/host_capability_schemas.py +64 -0
- agent_lifecycle/contracts/import_dialect_schemas.py +166 -0
- agent_lifecycle/contracts/metric_schemas.py +315 -0
- agent_lifecycle/contracts/paths.py +31 -0
- agent_lifecycle/contracts/plan_contract_schemas.py +148 -0
- agent_lifecycle/contracts/policy_schemas.py +350 -0
- agent_lifecycle/contracts/progress_bridge_schemas.py +114 -0
- agent_lifecycle/contracts/progress_hook_schemas.py +119 -0
- agent_lifecycle/contracts/proof_integrity_schemas.py +240 -0
- agent_lifecycle/contracts/release_contract_schemas.py +476 -0
- agent_lifecycle/contracts/review_mesh_recommendation_schemas.py +64 -0
- agent_lifecycle/contracts/review_mesh_schemas.py +222 -0
- agent_lifecycle/contracts/review_quality_schemas.py +109 -0
- agent_lifecycle/contracts/runner_schemas.py +91 -0
- agent_lifecycle/contracts/runner_worktree_schemas.py +389 -0
- agent_lifecycle/contracts/sandbox_schemas.py +159 -0
- agent_lifecycle/contracts/schema_builders.py +23 -0
- agent_lifecycle/contracts/schemas.py +93 -0
- agent_lifecycle/contracts/status_goal_schemas.py +469 -0
- agent_lifecycle/contracts/task_template_schemas.py +96 -0
- agent_lifecycle/contracts/usage_export_schemas.py +68 -0
- agent_lifecycle/diagnostics/__init__.py +17 -0
- agent_lifecycle/diagnostics/bundles.py +124 -0
- agent_lifecycle/diagnostics/readiness.py +733 -0
- agent_lifecycle/evidence_index/__init__.py +39 -0
- agent_lifecycle/evidence_index/core.py +272 -0
- agent_lifecycle/evidence_index/episode_index.py +329 -0
- agent_lifecycle/evidence_index/external_context.py +276 -0
- agent_lifecycle/followup/__init__.py +23 -0
- agent_lifecycle/followup/records.py +404 -0
- agent_lifecycle/freeze/__init__.py +5 -0
- agent_lifecycle/freeze/locks.py +23 -0
- agent_lifecycle/goal/__init__.py +15 -0
- agent_lifecycle/goal/records.py +244 -0
- agent_lifecycle/goal/view.py +109 -0
- agent_lifecycle/host_protocol/__init__.py +65 -0
- agent_lifecycle/host_protocol/acp_capability.py +195 -0
- agent_lifecycle/host_protocol/capabilities.py +268 -0
- agent_lifecycle/host_protocol/contracts.py +214 -0
- agent_lifecycle/host_protocol/event_capture.py +390 -0
- agent_lifecycle/host_protocol/events.py +196 -0
- agent_lifecycle/host_protocol/inspection.py +194 -0
- agent_lifecycle/host_protocol/inspection_common.py +310 -0
- agent_lifecycle/host_protocol/inspection_cursor.py +164 -0
- agent_lifecycle/host_protocol/inspection_gemini_cli.py +163 -0
- agent_lifecycle/host_protocol/inspection_hermes.py +161 -0
- agent_lifecycle/host_protocol/inspection_kimi_code.py +169 -0
- agent_lifecycle/host_protocol/inspection_opencode.py +147 -0
- agent_lifecycle/host_protocol/inspection_qwen_code.py +143 -0
- agent_lifecycle/host_protocol/receipts.py +58 -0
- agent_lifecycle/host_protocol/scaffold.py +329 -0
- agent_lifecycle/host_protocol/validation.py +229 -0
- agent_lifecycle/imports/__init__.py +76 -0
- agent_lifecycle/imports/agentskills_profile.py +54 -0
- agent_lifecycle/imports/bmad_profile.py +49 -0
- agent_lifecycle/imports/constitution_adr.py +91 -0
- agent_lifecycle/imports/dialect_profiles.py +121 -0
- agent_lifecycle/imports/external_agent.py +28 -0
- agent_lifecycle/imports/external_dialects.py +486 -0
- agent_lifecycle/imports/external_workflow.py +28 -0
- agent_lifecycle/imports/multi_markdown.py +111 -0
- agent_lifecycle/imports/openspec_profile.py +49 -0
- agent_lifecycle/imports/planning.py +536 -0
- agent_lifecycle/imports/spec_kit_profile.py +49 -0
- agent_lifecycle/imports/spec_kitty_profile.py +49 -0
- agent_lifecycle/metrics/__init__.py +60 -0
- agent_lifecycle/metrics/cost_collection.py +315 -0
- agent_lifecycle/metrics/costs.py +203 -0
- agent_lifecycle/metrics/outcome_index.py +330 -0
- agent_lifecycle/metrics/phase_resources.py +208 -0
- agent_lifecycle/metrics/recommendations.py +507 -0
- agent_lifecycle/metrics/regression_signals.py +64 -0
- agent_lifecycle/metrics/usage_export.py +390 -0
- agent_lifecycle/model_routing/__init__.py +19 -0
- agent_lifecycle/model_routing/profiles.py +323 -0
- agent_lifecycle/model_routing/receipts.py +140 -0
- agent_lifecycle/model_routing/resolver.py +566 -0
- agent_lifecycle/neutrality/__init__.py +12 -0
- agent_lifecycle/neutrality/__main__.py +6 -0
- agent_lifecycle/neutrality/authority.py +265 -0
- agent_lifecycle/neutrality/canonical.py +28 -0
- agent_lifecycle/neutrality/cli.py +294 -0
- agent_lifecycle/neutrality/ed25519.py +124 -0
- agent_lifecycle/neutrality/errors.py +89 -0
- agent_lifecycle/neutrality/gate.py +269 -0
- agent_lifecycle/neutrality/paths.py +92 -0
- agent_lifecycle/neutrality/policy.py +76 -0
- agent_lifecycle/neutrality/receipt.py +135 -0
- agent_lifecycle/neutrality/scanner.py +310 -0
- agent_lifecycle/neutrality/test_runner.py +116 -0
- agent_lifecycle/planning/__init__.py +47 -0
- agent_lifecycle/planning/acceptance_markdown.py +159 -0
- agent_lifecycle/planning/completeness.py +362 -0
- agent_lifecycle/planning/continuity.py +255 -0
- agent_lifecycle/planning/templates.py +294 -0
- agent_lifecycle/planning/tiering.py +87 -0
- agent_lifecycle/planning/validation.py +72 -0
- agent_lifecycle/policy/__init__.py +37 -0
- agent_lifecycle/policy/adaptive_lifecycle.py +416 -0
- agent_lifecycle/policy/apply.py +44 -0
- agent_lifecycle/policy/proposals.py +189 -0
- agent_lifecycle/policy/quality_floor.py +144 -0
- agent_lifecycle/policy/runtime_receipts.py +160 -0
- agent_lifecycle/py.typed +1 -0
- agent_lifecycle/quality/__init__.py +89 -0
- agent_lifecycle/quality/bug_forensics.py +727 -0
- agent_lifecycle/quality/bug_forensics_advisor.py +88 -0
- agent_lifecycle/quality/bug_forensics_recipes.py +239 -0
- agent_lifecycle/quality/cross_check.py +437 -0
- agent_lifecycle/quality/failure_classification.py +318 -0
- agent_lifecycle/quality/packs.py +265 -0
- agent_lifecycle/reporting/__init__.py +37 -0
- agent_lifecycle/reporting/change_summary.py +156 -0
- agent_lifecycle/reporting/event_feed.py +101 -0
- agent_lifecycle/reporting/progress_bridge.py +153 -0
- agent_lifecycle/reporting/progress_hooks.py +161 -0
- agent_lifecycle/reporting/progress_terminal.py +95 -0
- agent_lifecycle/reporting/progress_view.py +284 -0
- agent_lifecycle/reporting/status_view.py +125 -0
- agent_lifecycle/reporting/usage_export.py +59 -0
- agent_lifecycle/review/__init__.py +5 -0
- agent_lifecycle/review/validation.py +29 -0
- agent_lifecycle/review_mesh/__init__.py +83 -0
- agent_lifecycle/review_mesh/assignments.py +160 -0
- agent_lifecycle/review_mesh/contracts.py +685 -0
- agent_lifecycle/review_mesh/operator_templates.py +320 -0
- agent_lifecycle/review_mesh/quorum.py +38 -0
- agent_lifecycle/review_mesh/recommendation.py +487 -0
- agent_lifecycle/review_mesh/results.py +98 -0
- agent_lifecycle/review_mesh/synthesis.py +102 -0
- agent_lifecycle/runner/__init__.py +47 -0
- agent_lifecycle/runner/attempt_snapshots.py +244 -0
- agent_lifecycle/runner/core.py +653 -0
- agent_lifecycle/runner/sandbox_receipts.py +628 -0
- agent_lifecycle/specification/__init__.py +23 -0
- agent_lifecycle/specification/completion_check.py +135 -0
- agent_lifecycle/specification/completion_gate.py +501 -0
- agent_lifecycle/specification/completion_signal.py +75 -0
- agent_lifecycle/specification/validation.py +48 -0
- agent_lifecycle/workflow/__init__.py +79 -0
- agent_lifecycle/workflow/artifacts.py +74 -0
- agent_lifecycle/workflow/budget_decisions.py +370 -0
- agent_lifecycle/workflow/budget_policy.py +140 -0
- agent_lifecycle/workflow/budget_receipts.py +70 -0
- agent_lifecycle/workflow/bug_forensics_gates.py +284 -0
- agent_lifecycle/workflow/controller.py +45 -0
- agent_lifecycle/workflow/events.py +50 -0
- agent_lifecycle/workflow/final_proof_integrity.py +74 -0
- agent_lifecycle/workflow/finalization.py +422 -0
- agent_lifecycle/workflow/gates.py +185 -0
- agent_lifecycle/workflow/implementation_audit_gate.py +126 -0
- agent_lifecycle/workflow/leases.py +220 -0
- agent_lifecycle/workflow/lineage.py +108 -0
- agent_lifecycle/workflow/managed_runner.py +167 -0
- agent_lifecycle/workflow/model_usage.py +106 -0
- agent_lifecycle/workflow/next_action.py +86 -0
- agent_lifecycle/workflow/operation_kernel.py +98 -0
- agent_lifecycle/workflow/plan_adoption.py +439 -0
- agent_lifecycle/workflow/query.py +52 -0
- agent_lifecycle/workflow/review_mesh_gate.py +93 -0
- agent_lifecycle/workflow/reviews.py +168 -0
- agent_lifecycle/workflow/run_transitions.py +163 -0
- agent_lifecycle/workflow/sandbox_policy.py +214 -0
- agent_lifecycle/workflow/selectors.py +57 -0
- agent_lifecycle/workflow/state.py +130 -0
- agent_lifecycle/workflow/task_transitions.py +421 -0
- agent_lifecycle/worktree/__init__.py +17 -0
- agent_lifecycle/worktree/isolation.py +407 -0
- agent_lifecycle_kit-1.50.1.dist-info/METADATA +201 -0
- agent_lifecycle_kit-1.50.1.dist-info/RECORD +227 -0
- agent_lifecycle_kit-1.50.1.dist-info/WHEEL +5 -0
- agent_lifecycle_kit-1.50.1.dist-info/entry_points.txt +3 -0
- agent_lifecycle_kit-1.50.1.dist-info/licenses/LICENSE +201 -0
- agent_lifecycle_kit-1.50.1.dist-info/licenses/NOTICE +5 -0
- 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
|