ms8 0.2.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 (232) hide show
  1. ms8/__init__.py +3 -0
  2. ms8/__main__.py +15 -0
  3. ms8/agent_native/__init__.py +6 -0
  4. ms8/agent_native/agent_cli.py +478 -0
  5. ms8/agent_native/onboarding.py +294 -0
  6. ms8/agent_native/permission.py +65 -0
  7. ms8/agent_native/report.py +16 -0
  8. ms8/agent_native/task_spec.py +11 -0
  9. ms8/agent_native/task_templates.py +259 -0
  10. ms8/app/__init__.py +5 -0
  11. ms8/app/classifier/__init__.py +3 -0
  12. ms8/app/classifier/context_builder.py +48 -0
  13. ms8/app/classifier/hybrid_classifier.py +82 -0
  14. ms8/app/classifier/llm_classifier.py +39 -0
  15. ms8/app/classifier/rule_classifier.py +27 -0
  16. ms8/app/classifier/threshold_manager.py +25 -0
  17. ms8/app/config.py +152 -0
  18. ms8/app/extractors/__init__.py +0 -0
  19. ms8/app/extractors/action_extractor.py +66 -0
  20. ms8/app/extractors/entity_extractor.py +121 -0
  21. ms8/app/extractors/technical_extractor.py +9 -0
  22. ms8/app/feedback/__init__.py +0 -0
  23. ms8/app/feedback/feedback_service.py +47 -0
  24. ms8/app/feedback/rule_optimizer.py +117 -0
  25. ms8/app/integrations/__init__.py +0 -0
  26. ms8/app/integrations/ollama_client.py +57 -0
  27. ms8/app/main.py +12 -0
  28. ms8/app/memory/__init__.py +0 -0
  29. ms8/app/memory/indexer.py +230 -0
  30. ms8/app/memory/models.py +9 -0
  31. ms8/app/memory/repository.py +142 -0
  32. ms8/app/memory/search.py +48 -0
  33. ms8/app/observability/__init__.py +0 -0
  34. ms8/app/observability/logger.py +44 -0
  35. ms8/app/observability/metrics.py +14 -0
  36. ms8/app/observability/trace.py +16 -0
  37. ms8/app/pipeline/__init__.py +37 -0
  38. ms8/app/pipeline/consistency.py +8 -0
  39. ms8/app/pipeline/decision.py +19 -0
  40. ms8/app/pipeline/dedupe.py +51 -0
  41. ms8/app/pipeline/memory_admission_engine.py +207 -0
  42. ms8/app/pipeline/memory_pipeline.py +414 -0
  43. ms8/app/pipeline/quality_gate.py +28 -0
  44. ms8/app/pipeline/risk_scoring.py +45 -0
  45. ms8/app/review/__init__.py +0 -0
  46. ms8/app/review/batch_review.py +112 -0
  47. ms8/app/review/review_service.py +82 -0
  48. ms8/app/rules/__init__.py +3 -0
  49. ms8/app/rules/base.py +53 -0
  50. ms8/app/rules/block_rules.py +177 -0
  51. ms8/app/rules/category_rules.py +58 -0
  52. ms8/app/rules/conflict_rules.py +72 -0
  53. ms8/app/rules/dedupe_rules.py +27 -0
  54. ms8/app/rules/extraction_rules.py +14 -0
  55. ms8/app/rules/preprocess_rules.py +41 -0
  56. ms8/app/rules/privacy_rules.py +99 -0
  57. ms8/app/rules/registry.py +19 -0
  58. ms8/app/rules/tag_rules.py +12 -0
  59. ms8/app/schemas/__init__.py +0 -0
  60. ms8/app/schemas/feedback_schema.py +16 -0
  61. ms8/app/schemas/pipeline_schema.py +70 -0
  62. ms8/app/schemas/review_schema.py +37 -0
  63. ms8/ask.py +41 -0
  64. ms8/cli.py +1657 -0
  65. ms8/compression_governance.py +133 -0
  66. ms8/connect/__init__.py +13 -0
  67. ms8/connect/adapter_registry/__init__.py +4 -0
  68. ms8/connect/adapter_registry/registry.py +92 -0
  69. ms8/connect/adapter_registry/scan_tools.py +23 -0
  70. ms8/connect/integration_hooks/service_models.py +23 -0
  71. ms8/connect/local_llm_adapter/__init__.py +1 -0
  72. ms8/connect/local_llm_adapter/adapter_llm.py +54 -0
  73. ms8/connect/mcp_server/__init__.py +13 -0
  74. ms8/connect/mcp_server/mcp_server.py +286 -0
  75. ms8/connect/mcp_server/memory_service_interface.py +537 -0
  76. ms8/connect/mcp_server/stdio_server.py +262 -0
  77. ms8/connect/scripts/__init__.py +2 -0
  78. ms8/connect/scripts/apply_client_configs.py +157 -0
  79. ms8/connect/scripts/bootstrap.py +523 -0
  80. ms8/connect/scripts/client_config.py +472 -0
  81. ms8/connect/scripts/common.py +118 -0
  82. ms8/connect/scripts/connect.py +196 -0
  83. ms8/connect/scripts/generate_client_configs.py +44 -0
  84. ms8/connect/scripts/install_env.py +20 -0
  85. ms8/connect/scripts/rollback_client_configs.py +108 -0
  86. ms8/connect/scripts/scan_register.py +26 -0
  87. ms8/connect/scripts/smoke_test.py +72 -0
  88. ms8/connect/scripts/status.py +49 -0
  89. ms8/connect/scripts/verify_client_configs.py +128 -0
  90. ms8/dashboard.py +227 -0
  91. ms8/demo.py +75 -0
  92. ms8/doctor.py +576 -0
  93. ms8/engine.py +666 -0
  94. ms8/engine_core/__init__.py +10 -0
  95. ms8/engine_core/admission_compat.py +78 -0
  96. ms8/engine_core/agent_skills_standard.py +399 -0
  97. ms8/engine_core/auto_memory.py +545 -0
  98. ms8/engine_core/built_in_skills.py +526 -0
  99. ms8/engine_core/config.py +1163 -0
  100. ms8/engine_core/context_material.py +424 -0
  101. ms8/engine_core/context_understanding.py +555 -0
  102. ms8/engine_core/core.py +4821 -0
  103. ms8/engine_core/enhanced_self_improvement.py +373 -0
  104. ms8/engine_core/enhanced_subagents.py +684 -0
  105. ms8/engine_core/expression_preference_profile.py +172 -0
  106. ms8/engine_core/file_store.py +54 -0
  107. ms8/engine_core/file_write_guard.py +108 -0
  108. ms8/engine_core/git_utils.py +211 -0
  109. ms8/engine_core/governance.py +168 -0
  110. ms8/engine_core/knowledge_arbitration.py +113 -0
  111. ms8/engine_core/knowledge_feedback.py +330 -0
  112. ms8/engine_core/knowledge_graph.py +3271 -0
  113. ms8/engine_core/knowledge_rules.py +54 -0
  114. ms8/engine_core/learning.py +983 -0
  115. ms8/engine_core/license.py +229 -0
  116. ms8/engine_core/local_llm.py +933 -0
  117. ms8/engine_core/maintenance/__init__.py +1 -0
  118. ms8/engine_core/maintenance/self_check/__init__.py +10 -0
  119. ms8/engine_core/maintenance/self_check/check_runner.py +405 -0
  120. ms8/engine_core/maintenance/self_check/check_specs.py +2921 -0
  121. ms8/engine_core/maintenance/self_check/reporter.py +884 -0
  122. ms8/engine_core/maintenance/self_repair/__init__.py +13 -0
  123. ms8/engine_core/maintenance/self_repair/repair_audit.py +151 -0
  124. ms8/engine_core/maintenance/self_repair/repair_cli.py +74 -0
  125. ms8/engine_core/maintenance/self_repair/repair_orchestrator.py +176 -0
  126. ms8/engine_core/maintenance/self_repair/repair_policies.py +986 -0
  127. ms8/engine_core/maintenance/self_repair/repair_runner.py +1037 -0
  128. ms8/engine_core/maintenance/self_repair/repair_schema.py +87 -0
  129. ms8/engine_core/maintenance/self_repair/repair_validator.py +33 -0
  130. ms8/engine_core/maintenance_manager.py +581 -0
  131. ms8/engine_core/maintenance_policy.py +808 -0
  132. ms8/engine_core/memory_blocks.py +110 -0
  133. ms8/engine_core/memory_section_parser.py +96 -0
  134. ms8/engine_core/meta_cognition.py +847 -0
  135. ms8/engine_core/metrics_contract.py +58 -0
  136. ms8/engine_core/monitoring.py +1103 -0
  137. ms8/engine_core/pattern_recognition.py +562 -0
  138. ms8/engine_core/policy_engine_iface.py +54 -0
  139. ms8/engine_core/policy_engine_loader.py +213 -0
  140. ms8/engine_core/policy_engine_open.py +273 -0
  141. ms8/engine_core/priority_engine.py +217 -0
  142. ms8/engine_core/record_gateway.py +46 -0
  143. ms8/engine_core/response_mode_router.py +336 -0
  144. ms8/engine_core/response_mode_types.py +77 -0
  145. ms8/engine_core/security/__init__.py +21 -0
  146. ms8/engine_core/security/cli.py +11 -0
  147. ms8/engine_core/security/crypto_manager.py +6 -0
  148. ms8/engine_core/security/encryption/__init__.py +15 -0
  149. ms8/engine_core/security/encryption/cli.py +86 -0
  150. ms8/engine_core/security/encryption/crypto_manager.py +267 -0
  151. ms8/engine_core/security/encryption/file_crypto.py +61 -0
  152. ms8/engine_core/security/encryption/key_manager.py +261 -0
  153. ms8/engine_core/security/encryption/recovery.py +23 -0
  154. ms8/engine_core/security/encryption/security_schema.py +55 -0
  155. ms8/engine_core/security/file_crypto.py +6 -0
  156. ms8/engine_core/security/key_manager.py +6 -0
  157. ms8/engine_core/security/recovery.py +6 -0
  158. ms8/engine_core/security/security_schema.py +6 -0
  159. ms8/engine_core/security/shadow/__init__.py +10 -0
  160. ms8/engine_core/security/shadow/shadow_audit.py +42 -0
  161. ms8/engine_core/security/shadow/shadow_capacity_guard.py +66 -0
  162. ms8/engine_core/security/shadow/shadow_checkpoint_guard.py +53 -0
  163. ms8/engine_core/security/shadow/shadow_cli.py +258 -0
  164. ms8/engine_core/security/shadow/shadow_control_gate.py +164 -0
  165. ms8/engine_core/security/shadow/shadow_fs_guard.py +25 -0
  166. ms8/engine_core/security/shadow/shadow_guard.py +1562 -0
  167. ms8/engine_core/security/shadow/shadow_ledger.py +723 -0
  168. ms8/engine_core/security/shadow/shadow_locking.py +80 -0
  169. ms8/engine_core/security/shadow/shadow_manifest_guard.py +244 -0
  170. ms8/engine_core/security/shadow/shadow_permissions.py +83 -0
  171. ms8/engine_core/security/shadow/shadow_platform_log.py +20 -0
  172. ms8/engine_core/security/shadow/shadow_quarantine.py +33 -0
  173. ms8/engine_core/security/shadow/shadow_recovery.py +145 -0
  174. ms8/engine_core/security/shadow/shadow_recovery_guard.py +272 -0
  175. ms8/engine_core/security/shadow/shadow_schema.py +101 -0
  176. ms8/engine_core/security/shadow/shadow_seal.py +297 -0
  177. ms8/engine_core/security/shadow/shadow_tokens.py +53 -0
  178. ms8/engine_core/self_improvement.py +674 -0
  179. ms8/engine_core/semantic_search.py +240 -0
  180. ms8/engine_core/skill_github_discovery.py +546 -0
  181. ms8/engine_core/skill_marketplace.py +342 -0
  182. ms8/engine_core/skill_search_index.py +410 -0
  183. ms8/engine_core/skills.py +549 -0
  184. ms8/engine_core/sqlite_store.py +385 -0
  185. ms8/engine_core/sticky_prompt_templates.py +85 -0
  186. ms8/engine_core/subagents.py +237 -0
  187. ms8/engine_core/synthetic_memory.py +1444 -0
  188. ms8/engine_core/tests/__init__.py +3 -0
  189. ms8/engine_core/tests/test_auto_memory.py +51 -0
  190. ms8/engine_core/tests/test_c_lifecycle_ops.py +90 -0
  191. ms8/engine_core/tests/test_compression.py +70 -0
  192. ms8/engine_core/tests/test_connect_target_profiles.py +187 -0
  193. ms8/engine_core/tests/test_encrypted_file_store.py +31 -0
  194. ms8/engine_core/tests/test_encrypted_repository.py +44 -0
  195. ms8/engine_core/tests/test_feedback_review_loop.py +67 -0
  196. ms8/engine_core/tests/test_llm_setup_runtime.py +46 -0
  197. ms8/engine_core/tests/test_local_overlay.py +28 -0
  198. ms8/engine_core/tests/test_maintenance_policy_kg_tiering.py +116 -0
  199. ms8/engine_core/tests/test_maintenance_policy_shadow.py +93 -0
  200. ms8/engine_core/tests/test_meta_cognition.py +43 -0
  201. ms8/engine_core/tests/test_monitoring_shadow_replay.py +304 -0
  202. ms8/engine_core/tests/test_monitoring_slo.py +64 -0
  203. ms8/engine_core/tests/test_pipeline_guardrails.py +57 -0
  204. ms8/engine_core/tests/test_priority_engine.py +39 -0
  205. ms8/engine_core/tests/test_security_enable.py +36 -0
  206. ms8/engine_core/tests/test_security_recovery.py +21 -0
  207. ms8/engine_core/tests/test_security_unlock.py +18 -0
  208. ms8/engine_core/tests/test_shadow_control_gate.py +128 -0
  209. ms8/engine_core/tests/test_shadow_hardening.py +156 -0
  210. ms8/engine_core/tests/test_shadow_system.py +196 -0
  211. ms8/engine_core/tests/test_synthetic_memory.py +123 -0
  212. ms8/engine_core/tests/test_synthetic_reasoning_mode.py +85 -0
  213. ms8/engine_core/tests/test_threshold_approval_integrity.py +200 -0
  214. ms8/engine_core/threshold_cli.py +98 -0
  215. ms8/engine_core/utils.py +74 -0
  216. ms8/engine_core/whoosh_search.py +213 -0
  217. ms8/engine_core/working_memory.py +286 -0
  218. ms8/lifecycle.py +245 -0
  219. ms8/onboarding.py +127 -0
  220. ms8/paths.py +71 -0
  221. ms8/record_policy.py +439 -0
  222. ms8/review_governance.py +232 -0
  223. ms8/runtime.py +2616 -0
  224. ms8/service.py +80 -0
  225. ms8/shortcut.py +76 -0
  226. ms8/watch.py +86 -0
  227. ms8-0.2.0.dist-info/METADATA +428 -0
  228. ms8-0.2.0.dist-info/RECORD +232 -0
  229. ms8-0.2.0.dist-info/WHEEL +5 -0
  230. ms8-0.2.0.dist-info/entry_points.txt +2 -0
  231. ms8-0.2.0.dist-info/licenses/LICENSE +21 -0
  232. ms8-0.2.0.dist-info/top_level.txt +1 -0
ms8/__init__.py ADDED
@@ -0,0 +1,3 @@
1
+ """MS8 package."""
2
+
3
+ __version__ = "0.2.0"
ms8/__main__.py ADDED
@@ -0,0 +1,15 @@
1
+ """Module entrypoint for python -m ms8."""
2
+
3
+ import sys
4
+
5
+ from .cli import main
6
+
7
+ if __name__ == "__main__":
8
+ code = int(main())
9
+ try:
10
+ sys.stdout.flush()
11
+ sys.stderr.flush()
12
+ except OSError:
13
+ # Best-effort flush at process exit; do not mask unrelated exceptions.
14
+ pass
15
+ raise SystemExit(code)
@@ -0,0 +1,6 @@
1
+ """MS8 agent-native onboarding package."""
2
+
3
+ from .agent_cli import run_agent_cli
4
+
5
+ __all__ = ["run_agent_cli"]
6
+
@@ -0,0 +1,478 @@
1
+ """CLI handlers for agent-native phase-1 commands."""
2
+
3
+ from __future__ import annotations
4
+
5
+ import json
6
+ from contextlib import redirect_stdout
7
+ from datetime import datetime, timezone
8
+ from io import StringIO
9
+ from pathlib import Path
10
+
11
+ from .onboarding import (
12
+ init_agent_native,
13
+ list_tasks,
14
+ migrate_policy_path,
15
+ read_permission,
16
+ remove_agent_native,
17
+ show_task,
18
+ verify_permission_schema,
19
+ verify_tasks,
20
+ )
21
+ from .report import block
22
+
23
+
24
+ def _bundle_dir() -> Path:
25
+ from ..runtime import get_runtime_dir
26
+
27
+ return get_runtime_dir() / "bug_reports"
28
+
29
+
30
+ def _utc_stamp() -> str:
31
+ return datetime.now(timezone.utc).strftime("%Y%m%dT%H%M%SZ")
32
+
33
+
34
+ def _write_text(path: Path, text: str) -> None:
35
+ path.parent.mkdir(parents=True, exist_ok=True)
36
+ path.write_text(text, encoding="utf-8")
37
+
38
+
39
+ def _build_bug_report_bundle(*, redact: bool = True) -> dict:
40
+ from ..doctor import run_doctor
41
+ from ..runtime import engine_status, export_support_bundle_runtime, get_runtime_dir
42
+
43
+ bundle_root = _bundle_dir() / _utc_stamp()
44
+ bundle_root.mkdir(parents=True, exist_ok=True)
45
+
46
+ # Minimal phase-1 files.
47
+ _write_text(bundle_root / "ms8_status.txt", json.dumps(engine_status(), ensure_ascii=False, indent=2))
48
+
49
+ doctor_file = bundle_root / "doctor_output.txt"
50
+ try:
51
+ # Keep behavior simple: execute doctor and only store exit code summary.
52
+ code = run_doctor()
53
+ _write_text(doctor_file, f"doctor_exit_code={code}\n")
54
+ except (OSError, RuntimeError) as exc: # pragma: no cover
55
+ _write_text(doctor_file, f"doctor_error={exc}\n")
56
+
57
+ perm = read_permission()
58
+ _write_text(
59
+ bundle_root / "agent_native_status.json",
60
+ json.dumps(
61
+ {
62
+ "status": perm.get("status", "MISSING"),
63
+ "policy_path": perm.get("policy_path", ""),
64
+ "permission_profile": (
65
+ perm.get("policy", {}).get("permission_profile", "N/A")
66
+ if isinstance(perm.get("policy"), dict)
67
+ else "N/A"
68
+ ),
69
+ },
70
+ ensure_ascii=False,
71
+ indent=2,
72
+ ),
73
+ )
74
+ _write_text(
75
+ bundle_root / "redaction_note.txt",
76
+ (
77
+ "This report is redacted.\n"
78
+ "No memory content, secrets, passwords, raw private documents, or shadow "
79
+ "system internals are included.\n"
80
+ "Upload is not performed automatically.\n"
81
+ ),
82
+ )
83
+ _write_text(
84
+ bundle_root / "system_info.json",
85
+ json.dumps(
86
+ {
87
+ "runtime_dir": str(get_runtime_dir()),
88
+ "timestamp": datetime.now(timezone.utc).isoformat(),
89
+ "redact": bool(redact),
90
+ },
91
+ ensure_ascii=False,
92
+ indent=2,
93
+ ),
94
+ )
95
+
96
+ # Also produce existing support bundle zip for maintainers.
97
+ support = export_support_bundle_runtime(
98
+ output=str(bundle_root / "support_bundle.zip"),
99
+ redact=bool(redact),
100
+ dry_run=False,
101
+ )
102
+ return {
103
+ "ok": True,
104
+ "status": "CREATED",
105
+ "bundle_path": str(bundle_root),
106
+ "included": [
107
+ "system_info.json",
108
+ "ms8_status.txt",
109
+ "doctor_output.txt",
110
+ "agent_native_status.json",
111
+ "redaction_note.txt",
112
+ "support_bundle.zip",
113
+ ],
114
+ "excluded_for_privacy": [
115
+ "memory content",
116
+ "api keys",
117
+ "passwords",
118
+ "tokens",
119
+ "shadow internals",
120
+ ],
121
+ "upload_performed": "NO",
122
+ "support_bundle": support,
123
+ }
124
+
125
+
126
+ def _run_check_flow(*, allow_repair_preview: bool = True) -> dict:
127
+ from ..doctor import run_doctor
128
+ from ..runtime import engine_status, self_repair_run_runtime
129
+
130
+ buf = StringIO()
131
+ doctor_code = 1
132
+ try:
133
+ with redirect_stdout(buf):
134
+ doctor_code = run_doctor()
135
+ except (OSError, RuntimeError) as exc: # pragma: no cover
136
+ return {
137
+ "ok": False,
138
+ "status": "FAIL",
139
+ "error_code": "E_DOCTOR_RUN_FAILED",
140
+ "reason": str(exc),
141
+ }
142
+ doctor_text = buf.getvalue()
143
+ status_obj = engine_status()
144
+ perm = read_permission()
145
+ policy = perm.get("policy", {}) if isinstance(perm.get("policy"), dict) else {}
146
+ profile = str(policy.get("permission_profile", "N/A"))
147
+
148
+ issue_found = (
149
+ ("Overall: degraded" in doctor_text)
150
+ or ("Overall: FAIL" in doctor_text)
151
+ or ("⚠️" in doctor_text)
152
+ or ("❌" in doctor_text)
153
+ )
154
+ repair_plan = "No repair needed."
155
+ repair_preview = None
156
+ if issue_found:
157
+ if profile == "TRUSTED_AGENT" and allow_repair_preview:
158
+ repair_preview = self_repair_run_runtime(mode="dry-run")
159
+ if bool(repair_preview.get("ok", False)):
160
+ repair_plan = "Dry-run repair preview generated."
161
+ else:
162
+ repair_plan = "Dry-run repair preview failed."
163
+ else:
164
+ repair_plan = "Upgrade to TRUSTED_AGENT to see repair preview."
165
+ return {
166
+ "ok": True,
167
+ "status": "PASS",
168
+ "error_code": "",
169
+ "doctor_exit_code": doctor_code,
170
+ "doctor_overall": "degraded" if doctor_code != 0 else "healthy",
171
+ "engine_available": bool(status_obj.get("available", False)),
172
+ "permission_profile": profile,
173
+ "issue_found": issue_found,
174
+ "repair_plan": repair_plan,
175
+ "repair_preview": repair_preview if isinstance(repair_preview, dict) else {},
176
+ }
177
+
178
+
179
+ def _run_report_flow(*, redact: bool = True) -> dict:
180
+ from ..doctor import run_doctor
181
+
182
+ buf = StringIO()
183
+ code = 1
184
+ with redirect_stdout(buf):
185
+ code = run_doctor()
186
+ doctor_text = buf.getvalue()
187
+ critical_issue = ("Overall: degraded" in doctor_text) or ("Overall: FAIL" in doctor_text)
188
+ bundle = None
189
+ if critical_issue:
190
+ bundle = _build_bug_report_bundle(redact=bool(redact))
191
+ return {
192
+ "ok": True,
193
+ "status": "PASS",
194
+ "error_code": "",
195
+ "doctor_exit_code": code,
196
+ "critical_issue": critical_issue,
197
+ "bug_bundle": bundle if isinstance(bundle, dict) else {"status": "not_required"},
198
+ }
199
+
200
+
201
+ def _run_install_flow(*, profile: str = "DEFAULT_SAFE", confirm: bool = False) -> dict:
202
+ from ..doctor import run_doctor
203
+ from ..runtime import engine_status
204
+
205
+ chosen = str(profile or "DEFAULT_SAFE").strip().upper()
206
+ if chosen not in {"DEFAULT_SAFE", "TRUSTED_AGENT"}:
207
+ chosen = "DEFAULT_SAFE"
208
+ # Default shape required by MS8_FIRST_INSTALL_REPORT.
209
+ report: dict = {
210
+ "ok": False,
211
+ "status": "FAIL",
212
+ "error_code": "E_INSTALL_FLOW_UNKNOWN",
213
+ "permission_profile": "",
214
+ "python_version": "UNKNOWN",
215
+ "pip_status": "UNKNOWN",
216
+ "ms8_already_installed": "UNKNOWN",
217
+ "install_status": "NOT_RUN",
218
+ "agent_init_status": "NOT_RUN",
219
+ "doctor_status": "NOT_RUN",
220
+ "ms8_status": "NOT_RUN",
221
+ "learned_tasks": "NOT_RUN",
222
+ "safety_notes": "",
223
+ "user_guide": "Run: python -m ms8 agent task show usage",
224
+ "next_action": "Run: python -m ms8 agent task show usage",
225
+ }
226
+ report["permission_profile"] = chosen
227
+
228
+ def _run(cmd: list[str]) -> tuple[int, str]:
229
+ import subprocess
230
+ import sys
231
+
232
+ normalized = list(cmd)
233
+ if normalized and normalized[0] == "python":
234
+ normalized[0] = sys.executable
235
+
236
+ p = subprocess.run(normalized, capture_output=True, text=True, check=False)
237
+ out = (p.stdout or "") + ("\n" + p.stderr if p.stderr else "")
238
+ return int(p.returncode), out.strip()
239
+
240
+ py_code, py_out = _run(["python", "--version"])
241
+ report["python_version"] = py_out.splitlines()[0] if py_code == 0 and py_out else "UNKNOWN"
242
+ pip_code, _ = _run(["python", "-m", "pip", "--version"])
243
+ report["pip_status"] = "PASS" if pip_code == 0 else "FAIL"
244
+
245
+ if chosen == "TRUSTED_AGENT" and not confirm:
246
+ report.update(
247
+ {
248
+ "status": "NEEDS_CONFIRM",
249
+ "error_code": "E_NEEDS_CONFIRM",
250
+ "install_status": "NOT_RUN",
251
+ "safety_notes": "Waiting for explicit TRUSTED_AGENT confirmation.",
252
+ "next_action": "Re-run with --confirm when using TRUSTED_AGENT.",
253
+ }
254
+ )
255
+ return report
256
+
257
+ version_code = 0
258
+ try:
259
+ version_code, _ = _run(["python", "-m", "ms8", "version"])
260
+ except (FileNotFoundError, OSError):
261
+ version_code = 1
262
+ already_installed = version_code == 0
263
+ report["ms8_already_installed"] = bool(already_installed)
264
+ report["install_status"] = "SKIPPED" if already_installed else "PASS"
265
+
266
+ init_out = init_agent_native(
267
+ profile=chosen,
268
+ cwd=Path.cwd(),
269
+ force=True,
270
+ dry_run=False,
271
+ confirm=bool(confirm),
272
+ )
273
+ report["agent_init_status"] = str(init_out.get("status", "FAIL")).upper()
274
+ if str(init_out.get("status", "")).upper() == "NEEDS_CONFIRM":
275
+ report.update(
276
+ {
277
+ "status": "NEEDS_CONFIRM",
278
+ "error_code": "E_NEEDS_CONFIRM",
279
+ "doctor_status": "NOT_RUN",
280
+ "ms8_status": "NOT_RUN",
281
+ "learned_tasks": "NOT_RUN",
282
+ "next_action": "Confirm TRUSTED_AGENT explicitly.",
283
+ }
284
+ )
285
+ return report
286
+
287
+ doctor_buf = StringIO()
288
+ doctor_code = 1
289
+ with redirect_stdout(doctor_buf):
290
+ doctor_code = run_doctor()
291
+ doctor_text = doctor_buf.getvalue()
292
+ if "Overall: healthy" in doctor_text:
293
+ report["doctor_status"] = "PASS"
294
+ elif "Overall: degraded" in doctor_text:
295
+ report["doctor_status"] = "WARN"
296
+ else:
297
+ report["doctor_status"] = "FAIL" if doctor_code != 0 else "PASS"
298
+ eng = engine_status()
299
+ report["ms8_status"] = "PASS" if bool(eng.get("available", False)) else "FAIL"
300
+ report["learned_tasks"] = "usage,ops,check,report"
301
+ report["ok"] = bool(report["agent_init_status"] in {"PASS", "ALREADY_INSTALLED"}) and report["ms8_status"] == "PASS"
302
+ report["error_code"] = "" if report["ok"] else "E_INSTALL_FLOW_PARTIAL"
303
+ report["status"] = "ALREADY_INSTALLED" if already_installed else ("PASS" if report["ok"] else "FAIL")
304
+ report["safety_notes"] = "Phase-1: real repair disabled; no shadow system access."
305
+ return report
306
+
307
+
308
+ def run_agent_cli(args) -> int:
309
+ def _emit(title: str, payload: dict, ok: bool) -> int:
310
+ base = {
311
+ "report_version": 1,
312
+ "status": payload.get("status", "PASS" if ok else "FAIL"),
313
+ "error_code": payload.get("error_code", "" if ok else "E_UNKNOWN"),
314
+ "next_action": payload.get("next_action", ""),
315
+ }
316
+ merged = {**base, **payload}
317
+ print(block(title, merged))
318
+ return 0 if ok else 1
319
+
320
+ cmd = str(getattr(args, "agent_cmd", "") or "")
321
+ cwd = Path.cwd()
322
+
323
+ if cmd == "init":
324
+ out = init_agent_native(
325
+ profile=str(getattr(args, "profile", "DEFAULT_SAFE")),
326
+ cwd=cwd,
327
+ force=bool(getattr(args, "force", False)),
328
+ dry_run=bool(getattr(args, "dry_run", False)),
329
+ confirm=bool(getattr(args, "confirm", False)),
330
+ )
331
+ out["next_action"] = "Run: python -m ms8 agent task show usage"
332
+ return _emit("MS8_AGENT_INIT_RESULT", out, str(out.get("status", "")).upper() == "PASS")
333
+
334
+ if cmd == "permission":
335
+ sub = str(getattr(args, "permission_cmd", "") or "")
336
+ if sub == "upgrade":
337
+ target = str(getattr(args, "to", "TRUSTED_AGENT") or "TRUSTED_AGENT")
338
+ out = init_agent_native(
339
+ profile=target,
340
+ cwd=cwd,
341
+ force=True,
342
+ dry_run=bool(getattr(args, "dry_run", False)),
343
+ confirm=bool(getattr(args, "confirm", False)),
344
+ )
345
+ return _emit("MS8_AGENT_PERMISSION", out, str(out.get("status", "")).upper() == "PASS")
346
+ perm = read_permission()
347
+ policy = perm.get("policy", {}) if isinstance(perm.get("policy"), dict) else {}
348
+ out = {
349
+ "report_version": 1,
350
+ "status": perm.get("status", "MISSING"),
351
+ "permission_profile": policy.get("permission_profile", "N/A"),
352
+ "agent_mode": policy.get("agent_mode", "N/A"),
353
+ "execution_boundary": policy.get("execution_boundary", "N/A"),
354
+ "policy_path": perm.get("policy_path", ""),
355
+ "allowed": [k for k, v in policy.items() if str(k).startswith("allow_") and bool(v)],
356
+ "denied": [k for k, v in policy.items() if str(k).startswith("deny_") and bool(v)],
357
+ "next_action": "Run: python -m ms8 agent permission upgrade --to TRUSTED_AGENT --confirm",
358
+ }
359
+ return _emit("MS8_AGENT_PERMISSION", out, str(out.get("status", "")).upper() == "PASS")
360
+
361
+ if cmd == "policy":
362
+ sub = str(getattr(args, "policy_cmd", "") or "")
363
+ if sub == "verify":
364
+ out = verify_permission_schema()
365
+ out["next_action"] = "Run: python -m ms8 agent init --profile DEFAULT_SAFE --force"
366
+ return _emit("MS8_AGENT_POLICY_VERIFY", out, bool(out.get("ok", False)))
367
+ print("ms8 agent policy: choose verify")
368
+ return 2
369
+
370
+ if cmd == "run":
371
+ sub = str(getattr(args, "run_cmd", "") or "")
372
+ if sub == "install":
373
+ out = _run_install_flow(
374
+ profile=str(getattr(args, "profile", "DEFAULT_SAFE")),
375
+ confirm=bool(getattr(args, "confirm", False)),
376
+ )
377
+ return _emit("MS8_FIRST_INSTALL_REPORT", out, bool(out.get("ok", False)))
378
+ if sub == "check":
379
+ out = _run_check_flow(allow_repair_preview=not bool(getattr(args, "no_repair_preview", False)))
380
+ out["next_action"] = "Run: python -m ms8 agent run report"
381
+ return _emit("MS8_AGENT_RUN_CHECK", out, bool(out.get("ok", False)))
382
+ if sub == "report":
383
+ out = _run_report_flow(redact=not bool(getattr(args, "no_redact", False)))
384
+ out["next_action"] = "If critical_issue=true, share bug bundle path with maintainer."
385
+ return _emit("MS8_AGENT_RUN_REPORT", out, bool(out.get("ok", False)))
386
+ if sub == "daily":
387
+ check = _run_check_flow(allow_repair_preview=not bool(getattr(args, "no_repair_preview", False)))
388
+ report = _run_report_flow(redact=not bool(getattr(args, "no_redact", False)))
389
+ ok = bool(check.get("ok", False)) and bool(report.get("ok", False))
390
+ verbose_output = bool(getattr(args, "verbose_output", False))
391
+ if verbose_output:
392
+ check_out: dict = check
393
+ report_out: dict = report
394
+ else:
395
+ check_out = {
396
+ "doctor_overall": check.get("doctor_overall", "unknown"),
397
+ "permission_profile": check.get("permission_profile", "N/A"),
398
+ "issue_found": bool(check.get("issue_found", False)),
399
+ "repair_plan": check.get("repair_plan", ""),
400
+ }
401
+ report_out = {
402
+ "critical_issue": bool(report.get("critical_issue", False)),
403
+ "bug_bundle_status": (
404
+ report.get("bug_bundle", {}).get("status", "not_required")
405
+ if isinstance(report.get("bug_bundle"), dict)
406
+ else "not_required"
407
+ ),
408
+ }
409
+ summary_8 = [
410
+ f"1.status={'PASS' if ok else 'FAIL'}",
411
+ f"2.doctor_overall={check.get('doctor_overall', 'unknown')}",
412
+ f"3.permission_profile={check.get('permission_profile', 'N/A')}",
413
+ f"4.issue_found={bool(check.get('issue_found', False))}",
414
+ f"5.repair_plan={check.get('repair_plan', '')}",
415
+ f"6.critical_issue={bool(report.get('critical_issue', False))}",
416
+ (
417
+ "7.bug_bundle_status="
418
+ + (
419
+ report.get("bug_bundle", {}).get("status", "not_required")
420
+ if isinstance(report.get("bug_bundle"), dict)
421
+ else "not_required"
422
+ )
423
+ ),
424
+ "8.next_action=If critical_issue=true share bug bundle path with maintainer.",
425
+ ]
426
+ out = {
427
+ "ok": ok,
428
+ "status": "PASS" if ok else "FAIL",
429
+ "error_code": "" if ok else "E_AGENT_DAILY_FAILED",
430
+ "check": check_out,
431
+ "report": report_out,
432
+ "verbose_output": verbose_output,
433
+ "summary_8": summary_8,
434
+ "next_action": "If report.critical_issue=true, share bug bundle path with maintainer.",
435
+ }
436
+ return _emit("MS8_AGENT_RUN_DAILY", out, ok)
437
+ print("ms8 agent run: choose install|check|report|daily")
438
+ return 2
439
+
440
+ if cmd == "task":
441
+ sub = str(getattr(args, "task_cmd", "") or "")
442
+ if sub == "list":
443
+ out = list_tasks(cwd)
444
+ return _emit("MS8_AGENT_TASK_LIST", out, bool(out.get("ok", False)))
445
+ if sub == "verify":
446
+ out = verify_tasks(cwd)
447
+ out["next_action"] = "Run: python -m ms8 agent init --force"
448
+ return _emit("MS8_AGENT_TASK_VERIFY", out, bool(out.get("ok", False)))
449
+ if sub == "show":
450
+ out = show_task(cwd, str(getattr(args, "name", "") or ""))
451
+ meta = dict(out)
452
+ meta.pop("content", None)
453
+ _emit("MS8_AGENT_TASK_SHOW", meta, bool(out.get("ok", False)))
454
+ if bool(out.get("ok", False)) and out.get("content"):
455
+ print(out["content"])
456
+ return 0 if bool(out.get("ok", False)) else 1
457
+ print("ms8 agent task: choose list|verify|show")
458
+ return 2
459
+
460
+ if cmd == "remove":
461
+ out = remove_agent_native(cwd)
462
+ return _emit("MS8_AGENT_REMOVE_RESULT", out, bool(out.get("ok", False)))
463
+
464
+ if cmd == "migrate-policy-path":
465
+ out = migrate_policy_path(
466
+ dry_run=bool(getattr(args, "dry_run", False)),
467
+ force=bool(getattr(args, "force", False)),
468
+ cleanup_legacy=bool(getattr(args, "cleanup_legacy", False)),
469
+ )
470
+ return _emit("MS8_AGENT_POLICY_MIGRATION", out, bool(out.get("ok", False)))
471
+
472
+ if cmd == "bug-report":
473
+ out = _build_bug_report_bundle(redact=bool(getattr(args, "redact", True)))
474
+ out["next_action"] = "Share bundle path with maintainer"
475
+ return _emit("MS8_BUG_REPORT", out, bool(out.get("ok", False)))
476
+
477
+ print("ms8 agent: choose init|permission|policy|run|task|remove|migrate-policy-path|bug-report")
478
+ return 2