fable-engine 1.3.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 (104) hide show
  1. fable_compressor.py +356 -0
  2. fable_engine/__init__.py +1 -0
  3. fable_engine/actions/__init__.py +291 -0
  4. fable_engine/actions/cas.py +182 -0
  5. fable_engine/actions/deliberation.py +523 -0
  6. fable_engine/actions/fleet.py +807 -0
  7. fable_engine/actions/lifecycle.py +298 -0
  8. fable_engine/actions/scrapers.py +116 -0
  9. fable_engine/actions/system3.py +815 -0
  10. fable_engine/browser.py +824 -0
  11. fable_engine/cas.py +974 -0
  12. fable_engine/fable_session.json +510 -0
  13. fable_engine/guards.py +283 -0
  14. fable_engine/schema.py +714 -0
  15. fable_engine/scrapers/__init__.py +32 -0
  16. fable_engine/scrapers/arxiv.py +115 -0
  17. fable_engine/scrapers/base.py +386 -0
  18. fable_engine/scrapers/github.py +129 -0
  19. fable_engine/scrapers/reddit.py +154 -0
  20. fable_engine/scrapers/web.py +120 -0
  21. fable_engine/scrapers/x.py +125 -0
  22. fable_engine/scrapers/youtube.py +132 -0
  23. fable_engine/server.py +414 -0
  24. fable_engine/session.py +1819 -0
  25. fable_engine/test_server.py +1362 -0
  26. fable_engine/updater.py +541 -0
  27. fable_engine-1.3.1.dist-info/LICENSE +22 -0
  28. fable_engine-1.3.1.dist-info/METADATA +173 -0
  29. fable_engine-1.3.1.dist-info/RECORD +104 -0
  30. fable_engine-1.3.1.dist-info/WHEEL +5 -0
  31. fable_engine-1.3.1.dist-info/entry_points.txt +5 -0
  32. fable_engine-1.3.1.dist-info/top_level.txt +6 -0
  33. fable_mode/__init__.py +3 -0
  34. fable_mode/__main__.py +4 -0
  35. fable_mode/adapters.py +1014 -0
  36. fable_mode/installer.py +553 -0
  37. fable_mode/launcher.py +437 -0
  38. fable_mode/manifest.py +142 -0
  39. fable_mode/resources.json +114 -0
  40. fable_mode/safety.py +103 -0
  41. fable_mode_entry.py +10 -0
  42. fable_v2/__init__.py +146 -0
  43. fable_v2/adapters.py +151 -0
  44. fable_v2/coder_fleet/__init__.py +100 -0
  45. fable_v2/coder_fleet/ast_tools.py +158 -0
  46. fable_v2/coder_fleet/compute.py +199 -0
  47. fable_v2/coder_fleet/design_engine.py +1316 -0
  48. fable_v2/coder_fleet/diagnostics.py +293 -0
  49. fable_v2/coder_fleet/fleet_dispatcher.py +214 -0
  50. fable_v2/coder_fleet/mock_auditor.py +306 -0
  51. fable_v2/coder_fleet/mutation.py +216 -0
  52. fable_v2/coder_fleet/property_oracle.py +260 -0
  53. fable_v2/coder_fleet/receipt_attestor.py +122 -0
  54. fable_v2/coder_fleet/red_team_swarm.py +908 -0
  55. fable_v2/coder_fleet/test_harness.py +198 -0
  56. fable_v2/coder_fleet/vector_engine.py +1287 -0
  57. fable_v2/coder_fleet/visual.py +357 -0
  58. fable_v2/coder_fleet/workspace.py +153 -0
  59. fable_v2/cortical/__init__.py +20 -0
  60. fable_v2/cortical/plasticity_engine.py +992 -0
  61. fable_v2/execution_broker.py +811 -0
  62. fable_v2/proof_engine.py +1141 -0
  63. fable_v2/protocol.py +485 -0
  64. fable_v2/runtime.py +1010 -0
  65. fable_v2/system3/__init__.py +204 -0
  66. fable_v2/system3/causal.py +558 -0
  67. fable_v2/system3/dialectical.py +577 -0
  68. fable_v2/system3/evolution.py +503 -0
  69. fable_v2/system3/executive.py +338 -0
  70. fable_v2/system3/free_energy.py +479 -0
  71. fable_v2/system3/hyperbolic.py +555 -0
  72. fable_v2/system3/induction.py +336 -0
  73. fable_v2/system3/kripke.py +548 -0
  74. fable_v2/system3/oracle.py +745 -0
  75. fable_v2/verifiers.py +72 -0
  76. tests/__init__.py +1 -0
  77. tests/test_anti_loop_circuit_breaker.py +64 -0
  78. tests/test_auto_updater.py +407 -0
  79. tests/test_coder_fleet.py +535 -0
  80. tests/test_delegation_compiler.py +54 -0
  81. tests/test_descriptor_boundaries.py +126 -0
  82. tests/test_design_engine.py +603 -0
  83. tests/test_epistemic_evidence_validator.py +66 -0
  84. tests/test_execution_broker.py +233 -0
  85. tests/test_fable_v2.py +406 -0
  86. tests/test_fleet_transitions.py +116 -0
  87. tests/test_fsm_redteam_evolution.py +406 -0
  88. tests/test_goal_rubric_and_pipeline.py +367 -0
  89. tests/test_hebbian_plasticity.py +585 -0
  90. tests/test_packaging_runtime.py +194 -0
  91. tests/test_proof_engine.py +259 -0
  92. tests/test_red_team_swarm.py +645 -0
  93. tests/test_redteam_remediation.py +169 -0
  94. tests/test_registration_transaction.py +375 -0
  95. tests/test_requested_regressions.py +467 -0
  96. tests/test_scrapers.py +370 -0
  97. tests/test_server_actions.py +93 -0
  98. tests/test_server_frontier_actions.py +269 -0
  99. tests/test_server_protocol.py +88 -0
  100. tests/test_stealth_browser.py +970 -0
  101. tests/test_system3.py +381 -0
  102. tests/test_system3_deep_integration.py +385 -0
  103. tests/test_system3_frontier.py +436 -0
  104. tests/test_vector_engine.py +608 -0
@@ -0,0 +1,523 @@
1
+ """Deliberation, epistemic ledger, invariants, refinement, and proof action handlers."""
2
+ from __future__ import annotations
3
+
4
+ import collections
5
+ import hashlib
6
+ import json
7
+ import logging
8
+ import math
9
+ import os
10
+ import re
11
+ import sys
12
+ import time
13
+ from pathlib import Path
14
+ from typing import Any, Dict, List, Optional, Tuple, Union
15
+
16
+ logger = logging.getLogger("fable-engine.actions.deliberation")
17
+
18
+ from fable_engine.guards import (
19
+ GLOBAL_VELOCITY_PROFILER,
20
+ DelegationContractCompiler,
21
+ )
22
+ from fable_engine.session import (
23
+ ACTIVE_SESSIONS,
24
+ PHASES,
25
+ SESSIONS_DIR,
26
+ SILENT_DELIBERATION_REMINDER,
27
+ FableSession,
28
+ SessionState,
29
+ _validate_session_name,
30
+ _validate_time_budget,
31
+ get_or_load_session,
32
+ )
33
+ # DeterministicProofValidator imported lazily
34
+
35
+ def _handle_log_epistemic_item(arguments: Dict[str, Any]) -> str:
36
+ action = arguments.get("action", "").strip().lower()
37
+ session_name = arguments.get("session_name", "").strip()
38
+ session: Optional[FableSession] = None
39
+ if not session_name:
40
+ return "Error: 'session_name' is required for action 'log_epistemic_item'."
41
+ tag = arguments.get("tag", "").strip()
42
+ if not tag:
43
+ return "Error: 'tag' (PROVEN, HYPOTHESIS, UNKNOWN) is required for 'log_epistemic_item'."
44
+ claim = arguments.get("claim", "").strip()
45
+ if not claim:
46
+ return "Error: 'claim' is required for 'log_epistemic_item'."
47
+ evidence = arguments.get("evidence")
48
+
49
+ session = get_or_load_session(session_name)
50
+ item = session.log_epistemic_item(tag, claim, evidence)
51
+ session.save()
52
+
53
+ tel = session.get_telemetry()
54
+ counts = tel["epistemic_counts"]
55
+
56
+ ev_display = f"\n- **Evidence**: `{item['evidence']}`" if item.get("evidence") else ""
57
+ return (
58
+ f"### 📝 Epistemic Item Logged (`{item['id']}`)\n\n"
59
+ f"- **Session**: `{session.session_name}`\n"
60
+ f"- **Classification**: `[{item['tag']}]`\n"
61
+ f"- **Claim**: {item['claim']}{ev_display}\n"
62
+ f"- **Logged in**: `{item['phase']}`\n"
63
+ f"- **Ledger Total**: `{counts['proven']} PROVEN`, `{counts['hypothesis']} HYPOTHESIS`, `{counts['unknown']} UNKNOWN`"
64
+ f"{SILENT_DELIBERATION_REMINDER if session.execution_locked else ''}"
65
+ )
66
+
67
+
68
+ def _handle_record_invariant(arguments: Dict[str, Any]) -> str:
69
+ action = arguments.get("action", "").strip().lower()
70
+ session_name = arguments.get("session_name", "").strip()
71
+ session: Optional[FableSession] = None
72
+ if not session_name:
73
+ return "Error: 'session_name' is required for action 'record_invariant'."
74
+ invariant_name = arguments.get("invariant_name", "").strip()
75
+ if not invariant_name:
76
+ return "Error: 'invariant_name' is required for 'record_invariant'."
77
+ formal_statement = arguments.get("formal_statement", "").strip()
78
+ if not formal_statement:
79
+ return "Error: 'formal_statement' is required for 'record_invariant'."
80
+ proof_or_rationale = arguments.get("proof_or_rationale", "").strip()
81
+ domain = arguments.get("domain", "architecture").strip()
82
+
83
+ session = get_or_load_session(session_name)
84
+ inv = session.record_invariant(invariant_name, formal_statement, proof_or_rationale, domain)
85
+ session.save()
86
+
87
+ return (
88
+ f"### 📐 Formal Invariant Recorded (`{inv['id']}`)\n\n"
89
+ f"- **Session**: `{session.session_name}`\n"
90
+ f"- **Invariant Name**: **{inv['name']}**\n"
91
+ f"- **Domain**: `{inv['domain'].upper()}`\n"
92
+ f"- **Formal Statement**: `{inv['formal_statement']}`\n"
93
+ f"- **Proof / Rationale**: {inv['proof_or_rationale']}\n"
94
+ f"- **Total Invariants**: `{len(session.invariants)}`"
95
+ f"{SILENT_DELIBERATION_REMINDER if session.execution_locked else ''}"
96
+ )
97
+
98
+
99
+ def _handle_log_refinement_cycle(arguments: Dict[str, Any]) -> str:
100
+ action = arguments.get("action", "").strip().lower()
101
+ session_name = arguments.get("session_name", "").strip()
102
+ session: Optional[FableSession] = None
103
+ if not session_name:
104
+ return "Error: 'session_name' is required for action 'log_refinement_cycle'."
105
+ refinement_type = arguments.get("refinement_type", "").strip()
106
+ if not refinement_type:
107
+ return "Error: 'refinement_type' is required for 'log_refinement_cycle'."
108
+ focus_area = arguments.get("focus_area", "").strip()
109
+ if not focus_area:
110
+ return "Error: 'focus_area' is required for 'log_refinement_cycle'."
111
+ critique_or_bottleneck = arguments.get("critique_or_bottleneck", "").strip()
112
+ if not critique_or_bottleneck:
113
+ return "Error: 'critique_or_bottleneck' is required for 'log_refinement_cycle'."
114
+ architectural_refinement = arguments.get("architectural_refinement", "").strip()
115
+ if not architectural_refinement:
116
+ return "Error: 'architectural_refinement' is required for 'log_refinement_cycle'."
117
+
118
+ terminal_probe_results = arguments.get("terminal_probe_results")
119
+ artifact_path = arguments.get("artifact_path")
120
+
121
+ session = get_or_load_session(session_name)
122
+ cycle = session.log_refinement_cycle(
123
+ refinement_type=refinement_type,
124
+ focus_area=focus_area,
125
+ critique_or_bottleneck=critique_or_bottleneck,
126
+ architectural_refinement=architectural_refinement,
127
+ terminal_probe_results=terminal_probe_results,
128
+ artifact_path=artifact_path
129
+ )
130
+ session.save()
131
+
132
+ tel = session.get_telemetry()
133
+ probe_display = f"\n- **Terminal Probes / Benchmarks**: `{cycle['terminal_probe_results']}`" if cycle.get("terminal_probe_results") else ""
134
+ artifact_display = f"\n- **Artifact Blueprint**: `{cycle['artifact_path']}`" if cycle.get("artifact_path") else ""
135
+
136
+ return (
137
+ f"### 🔄 Rethink-Refine Cycle #{cycle['cycle_number']} Logged\n\n"
138
+ f"- **Session**: `{session.session_name}`\n"
139
+ f"- **Refinement Type**: `{cycle['refinement_type'].upper()}`\n"
140
+ f"- **Focus Area**: {cycle['focus_area']}\n"
141
+ f"- **Critique / Bottleneck**: {cycle['critique_or_bottleneck']}\n"
142
+ f"- **Architectural Refinement**: {cycle['architectural_refinement']}{probe_display}{artifact_display}\n"
143
+ f"- **Phase**: `{cycle['phase']}`\n"
144
+ f"- **Pacing Remaining**: `{tel['remaining_formatted']}` ({tel['pacing_percentage']} budget used)\n"
145
+ f"- **Total Refinement Cycles**: `{len(session.refinement_cycles)}`\n\n"
146
+ f"> [!TIP]\n"
147
+ f"> Rethink-Refine Cognitive Loop active. Continue exploring alternative archetypes, falsifications, and terminal benchmarks until the time budget is fulfilled."
148
+ f"{SILENT_DELIBERATION_REMINDER if session.execution_locked else ''}"
149
+ )
150
+
151
+
152
+ def _handle_compile_delegation_contract(arguments: Dict[str, Any]) -> str:
153
+ action = arguments.get("action", "").strip().lower()
154
+ session_name = arguments.get("session_name", "").strip()
155
+ session: Optional[FableSession] = None
156
+ prompt = arguments.get("subagent_prompt") or arguments.get("prompt") or arguments.get("contract") or ""
157
+ if not str(prompt).strip():
158
+ return "Error: 'subagent_prompt' (or 'prompt') is required for action 'compile_delegation_contract'."
159
+
160
+ compiler = DelegationContractCompiler()
161
+ is_valid, errors, parsed = compiler.compile_and_validate(prompt)
162
+
163
+ if not is_valid:
164
+ err_list = "\n".join([f"- ❌ {e}" for e in errors])
165
+ return (
166
+ f"### 🛑 Subagent Delegation Contract Compilation Failed\n\n"
167
+ f"The subagent prompt does not satisfy the strict Fable-Mode delegation boundaries:\n\n"
168
+ f"{err_list}\n\n"
169
+ f"> [!WARNING]\n"
170
+ f"> Worker subagents must receive 100% bounded, unambiguous contracts before dispatch.\n"
171
+ f"> Ensure your prompt contains:\n"
172
+ f"> 1. `TargetFile: <file path>`\n"
173
+ f"> 2. `InterfaceContract: <type/function signature>`\n"
174
+ f"> 3. `StrictConstraints: <invariants / bounds>`\n"
175
+ f"> 4. `VerificationCommand: <exact CLI test command>`"
176
+ )
177
+
178
+ compiled_scaffold = parsed.get("compiled_prompt", prompt)
179
+ return (
180
+ f"### ✅ Subagent Delegation Contract Compiled Successfully (with System 3 Micro-Scaffolds)\n\n"
181
+ f"- **Target File**: `{parsed.get('TargetFile', 'Declared')}`\n"
182
+ f"- **Verification Command**: `{parsed.get('VerificationCommand', 'Declared')}`\n"
183
+ f"- **Contract Status**: `100% BOUNDED & VALIDATED`\n"
184
+ f"- **System 3 Micro-Scaffolds**: `INJECTED (Kripke AG(safe), Causal do(·) bounds, TRIZ Transcendence, Regex Constraints)`\n"
185
+ f"- **Dispatch Readiness**: `READY_FOR_SUBAGENT_DISPATCH` 🚀\n\n"
186
+ f"```markdown\n{compiled_scaffold}\n```\n\n"
187
+ f"> [!TIP]\n"
188
+ f"> You may now dispatch a worker subagent (`type: self`) with this validated contract once execution is unlocked."
189
+ )
190
+
191
+
192
+ def _handle_track_file_change(arguments: Dict[str, Any]) -> str:
193
+ action = arguments.get("action", "").strip().lower()
194
+ session_name = arguments.get("session_name", "").strip()
195
+ session: Optional[FableSession] = None
196
+ if not session_name:
197
+ return "Error: 'session_name' is required for action 'track_file_change'."
198
+ file_path = arguments.get("file_path", "").strip()
199
+ if not file_path:
200
+ return "Error: 'file_path' is required for action 'track_file_change'."
201
+ change_type = arguments.get("change_type", "").strip().lower()
202
+ if not change_type:
203
+ return "Error: 'change_type' ('modified', 'created', 'deleted', 'slated') is required for action 'track_file_change'."
204
+ diff_summary = arguments.get("diff_summary", "").strip()
205
+ if not diff_summary:
206
+ return "Error: 'diff_summary' is required for action 'track_file_change'."
207
+ rationale = arguments.get("rationale", "").strip()
208
+ affected_invariants = arguments.get("affected_invariants")
209
+
210
+ session = get_or_load_session(session_name)
211
+ entry = session.track_file_change(file_path, change_type, diff_summary, rationale, affected_invariants)
212
+ if getattr(session, "fable_run", None):
213
+ try:
214
+ from fable_v2.protocol import FileChangeRecord
215
+ session.fable_run.record_file_change(
216
+ FileChangeRecord(
217
+ file_path=file_path,
218
+ change_type=change_type,
219
+ before_hash=entry.get("before_hash"),
220
+ after_hash=entry.get("after_hash", ""),
221
+ diff_summary=diff_summary,
222
+ rationale=rationale or "",
223
+ affected_invariants=tuple(affected_invariants or []),
224
+ )
225
+ )
226
+ except Exception:
227
+ pass
228
+ session.save()
229
+
230
+ inv_str = f"\n- **Affected Invariants**: `{', '.join(entry['affected_invariants'])}`" if entry.get("affected_invariants") else ""
231
+ sha_str = f"\n- **File SHA256**: `{entry['sha256']}`" if entry.get("sha256") else ""
232
+ return (
233
+ f"### 📂 File Change Tracked\n\n"
234
+ f"- **Session**: `{session.session_name}`\n"
235
+ f"- **Target File**: `{entry['file_path']}`\n"
236
+ f"- **Change Type**: `{entry['change_type'].upper()}`\n"
237
+ f"- **Diff Summary**: {entry['diff_summary']}\n"
238
+ f"- **Rationale**: {entry['rationale'] or 'N/A'}"
239
+ f"{sha_str}"
240
+ f"{inv_str}\n"
241
+ f"- **Total Tracked Changes**: `{len(session.file_changes)}`"
242
+ f"{SILENT_DELIBERATION_REMINDER if session.execution_locked else ''}"
243
+ )
244
+
245
+
246
+ def _handle_get_session_lineage(arguments: Dict[str, Any]) -> str:
247
+ action = arguments.get("action", "").strip().lower()
248
+ session_name = arguments.get("session_name", "").strip()
249
+ session: Optional[FableSession] = None
250
+ if not session_name:
251
+ return "Error: 'session_name' is required for action 'get_session_lineage'."
252
+ session = get_or_load_session(session_name)
253
+ tel = session.get_telemetry()
254
+ v_prof = tel.get("velocity_profile", {})
255
+
256
+ # 1. Past files modified/created/deleted
257
+ past_files = [fc for fc in session.file_changes if fc.get("change_type") != "slated"]
258
+ past_lines = []
259
+ for pf in past_files:
260
+ sha = f" (`{pf['sha256'][:8]}`)" if pf.get("sha256") else ""
261
+ past_lines.append(f"- `[{pf['change_type'].upper()}]` `{pf['file_path']}`{sha}: {pf['diff_summary']}")
262
+ past_str = "\n".join(past_lines) if past_lines else "- No files modified or created yet."
263
+
264
+ # 2. Slated files
265
+ slated_files = [fc for fc in session.file_changes if fc.get("change_type") == "slated"]
266
+ slated_lines = []
267
+ for sf in slated_files:
268
+ slated_lines.append(f"- `[SLATED]` `{sf['file_path']}`: {sf['diff_summary']} (Rationale: {sf.get('rationale', 'N/A')})")
269
+ slated_str = "\n".join(slated_lines) if slated_lines else "- No upcoming files slated."
270
+
271
+ # 3. Roadmap & Phase History
272
+ phase_lines = []
273
+ for ph in session.phase_history:
274
+ phase_lines.append(f"- **{ph['phase']}**: {ph.get('summary', 'Entered')}")
275
+ roadmap_str = "\n".join(phase_lines)
276
+
277
+ # 4. Epistemic ledger
278
+ epi_lines = []
279
+ for item in session.epistemic_ledger:
280
+ rcpt = f" (Receipt: `{item['proof_receipt']['receipt_id']}`)" if item.get("proof_receipt") else ""
281
+ epi_lines.append(f"- `[{item['tag']}]` **{item['id']}**: {item['claim']}{rcpt}")
282
+ epi_str = "\n".join(epi_lines) if epi_lines else "- No epistemic items logged."
283
+
284
+ # 5. Invariants
285
+ inv_lines = []
286
+ for inv in session.invariants:
287
+ rcpt = f" (Receipt: `{inv['proof_receipt']['receipt_id']}`)" if inv.get("proof_receipt") else ""
288
+ inv_lines.append(f"- **{inv['name']}** `[{inv['domain']}]`: `{inv['formal_statement']}`{rcpt}")
289
+ inv_str = "\n".join(inv_lines) if inv_lines else "- No formal invariants recorded."
290
+
291
+ # 6. Visual mockups
292
+ vm = session.visual_mockups if isinstance(session.visual_mockups, dict) else {}
293
+ mockups_list = vm.get("mockups", [])
294
+ vm_lines = []
295
+ for m in mockups_list:
296
+ sel = " 🌟 *(SELECTED)*" if m.get("concept_name") == vm.get("selected_concept") else ""
297
+ vm_lines.append(f"- **{m.get('concept_name', 'Concept')}** `[{m.get('aesthetic_archetype', 'N/A')}]`{sel}: Palette: {m.get('palette', 'N/A')}, Typography: {m.get('typography', 'N/A')}")
298
+ vm_str = "\n".join(vm_lines) if vm_lines else "- No visual mockups recorded."
299
+
300
+ return (
301
+ f"### 🌐 Omniscient Session Lineage (`{session.session_name}`)\n\n"
302
+ f"#### 🎯 Mission Objective & Roadmap:\n"
303
+ f"- **Goal**: {session.objective}\n"
304
+ f"- **Active Phase**: `{session.active_phase}` (Phase {tel['phase_index']}/{tel['total_phases']})\n"
305
+ f"- **Pacing Remaining**: `{tel['pacing_remaining_formatted']}` / Authority: `{tel['authority_remaining_formatted']}`\n\n"
306
+ f"#### 🛣️ Phase Progression History:\n{roadmap_str}\n\n"
307
+ f"#### 📝 Completed File Mutations ({len(past_files)}):\n{past_str}\n\n"
308
+ f"#### 📋 Slated File Modifications ({len(slated_files)}):\n{slated_str}\n\n"
309
+ f"#### 🔬 Epistemic Grounding Ledger ({len(session.epistemic_ledger)} items):\n{epi_str}\n\n"
310
+ f"#### 📐 Formal Invariants & Contract Verification ({len(session.invariants)} items):\n{inv_str}\n\n"
311
+ f"#### 🎨 Visual Mockup Concepts & Spatial Spec:\n{vm_str}\n\n"
312
+ f"#### ⚡ Model Velocity & Capability Telemetry:\n"
313
+ f"- **Tier**: `{v_prof.get('model_tier', 'pro').upper()}` (Multiplier: `{v_prof.get('tier_multiplier', 1.0)}x`)\n"
314
+ f"- **Velocity**: `{v_prof.get('tokens_per_sec', 0.0)} est. tokens/sec` (`{v_prof.get('chars_per_sec', 0.0)} chars/sec`)\n"
315
+ f"- **Call Frequency**: `{v_prof.get('tool_call_frequency_cpm', 0.0)} calls/min` (Avg Interval: `{v_prof.get('avg_interval_seconds', 0.0)}s`)\n"
316
+ f"- **Total Ingested**: `{v_prof.get('total_requests', 0)} calls` / `{v_prof.get('total_estimated_tokens', 0)} est. tokens`"
317
+ f"{SILENT_DELIBERATION_REMINDER if session.execution_locked else ''}"
318
+ )
319
+
320
+
321
+ def _handle_inspect_plan(arguments: Dict[str, Any]) -> str:
322
+ action = arguments.get("action", "").strip().lower()
323
+ session_name = arguments.get("session_name", "").strip()
324
+ session: Optional[FableSession] = None
325
+ if not session_name:
326
+ return "Error: 'session_name' is required for action 'inspect_plan'."
327
+ session = get_or_load_session(session_name)
328
+ tel = session.get_telemetry()
329
+ gate_report = session._gate_report()
330
+
331
+ min_refinements = max(2, math.ceil(session.time_budget_minutes / 5.0))
332
+ current_refinements = len(session.refinement_cycles)
333
+ refinement_ok = current_refinements >= min_refinements
334
+
335
+ # Refinement history
336
+ ref_lines = []
337
+ for ref in session.refinement_cycles:
338
+ ref_lines.append(f"- **Cycle #{ref['cycle_number']}** `[{ref['refinement_type'].upper()}]` ({ref['focus_area']}): {ref['architectural_refinement']}")
339
+ ref_str = "\n".join(ref_lines) if ref_lines else "- No rethink-refine cycles logged yet."
340
+
341
+ # Slated files
342
+ slated_files = [fc for fc in session.file_changes if fc.get("change_type") == "slated"]
343
+ slated_lines = []
344
+ for sf in slated_files:
345
+ slated_lines.append(f"- `{sf['file_path']}`: {sf['diff_summary']}")
346
+ slated_str = "\n".join(slated_lines) if slated_lines else "- None declared yet."
347
+
348
+ # Gate checklist
349
+ c = gate_report["checks"]
350
+ gate_checklist = (
351
+ f"- [{'x' if c['two_proven_evidence_items'] else ' '}] At least 2 [PROVEN] facts with evidence ({gate_report['proven_with_evidence']}/2)\n"
352
+ f"- [{'x' if c['one_proved_invariant'] else ' '}] At least 1 formal Invariant with proof/rationale ({gate_report['invariants_with_proof']}/1)\n"
353
+ f"- [{'x' if c['adversarial_phase_reached'] else ' '}] Active Phase >= Phase 3 (Current: Phase {tel['phase_index']})\n"
354
+ f"- [{'x' if refinement_ok else ' '}] Anti-Idle Refinement Cycles ({current_refinements}/{min_refinements} required)\n"
355
+ f"- [{'x' if not session.execution_locked else ' '}] Immutable Authority Deadline Elapsed ({tel['authority_remaining_formatted']} remaining)"
356
+ )
357
+
358
+ delegation_guidelines = (
359
+ "1. Verify execution is unlocked (`can_execute_code: True`).\n"
360
+ "2. Compile Subagent Delegation Contracts with explicit `TargetFile`, `InterfaceContract`, `StrictConstraints`, and `VerificationCommand`.\n"
361
+ "3. Dispatch subagents to perform atomic codebase changes.\n"
362
+ "4. Enforce DoD validation via automated test suite execution."
363
+ )
364
+
365
+ return (
366
+ f"### 📋 Fable Execution Plan & Cognitive Blueprint (`{session.session_name}`)\n\n"
367
+ f"- **Objective**: {session.objective}\n"
368
+ f"- **Active Phase**: `{session.active_phase}`\n"
369
+ f"- **Execution Lock**: `{'🔴 LOCKED' if session.execution_locked else '🟢 UNLOCKED'}`\n\n"
370
+ f"#### 🚦 Cognitive Gate Status:\n{gate_checklist}\n\n"
371
+ f"#### 🔄 Rethink-Refine History ({current_refinements} cycles):\n{ref_str}\n\n"
372
+ f"#### 🛠️ Slated File Implementations:\n{slated_str}\n\n"
373
+ f"#### 🤖 Subagent Delegation & Implementer Instructions:\n{delegation_guidelines}"
374
+ f"{SILENT_DELIBERATION_REMINDER if session.execution_locked else ''}"
375
+ )
376
+
377
+
378
+ def _handle_verify_proof(arguments: Dict[str, Any]) -> str:
379
+ action = arguments.get("action", "").strip().lower()
380
+ session_name = arguments.get("session_name", "").strip()
381
+ session: Optional[FableSession] = None
382
+ claim = arguments.get("claim", "").strip()
383
+ if not claim:
384
+ return "Error: 'claim' is required for action 'verify_proof'."
385
+ proof_type = arguments.get("proof_type", "").strip().lower()
386
+ if not proof_type:
387
+ return "Error: 'proof_type' ('ast', 'receipt', 'file_sha256', 'formal_logic', 'vector_coordinates') is required for 'verify_proof'."
388
+ evidence = arguments.get("evidence", "")
389
+ target_resource = arguments.get("target_resource")
390
+
391
+ from fable_v2.proof_engine import DeterministicProofValidator
392
+ validator = DeterministicProofValidator()
393
+ result = validator.verify_proof(claim=claim, proof_type=proof_type, evidence=str(evidence), target_resource=target_resource)
394
+
395
+ if session_name:
396
+ try:
397
+ session = get_or_load_session(session_name)
398
+ session.proof_receipts.append(result)
399
+ if getattr(session, "fable_run", None):
400
+ try:
401
+ from fable_v2.protocol import ToolReceipt
402
+ session.fable_run.record_receipt(
403
+ ToolReceipt(
404
+ tool_name=f"proof_{proof_type}",
405
+ args={"claim": claim, "target_resource": target_resource},
406
+ output=result,
407
+ success=bool(result.get("verified")),
408
+ session_id=session.session_id,
409
+ )
410
+ )
411
+ except Exception:
412
+ pass
413
+ session.save()
414
+ except Exception:
415
+ pass
416
+
417
+ status_badge = "✅ VERIFIED" if result.get("verified") else "❌ FAILED"
418
+ err_msg = f"\n- **Error**: {result['error']}" if result.get("error") else ""
419
+ details_msg = f"\n- **Details**: {result['details']}" if result.get("details") else ""
420
+ return (
421
+ f"### ⚖️ Deterministic Proof Verification\n\n"
422
+ f"- **Status**: `{status_badge}`\n"
423
+ f"- **Receipt ID**: `{result.get('receipt_id')}`\n"
424
+ f"- **Proof Type**: `{result.get('proof_type')}`\n"
425
+ f"- **Claim**: {result.get('claim')}\n"
426
+ f"- **Timestamp**: `{time.ctime(result.get('timestamp', time.time()))}`"
427
+ f"{err_msg}"
428
+ f"{details_msg}"
429
+ )
430
+
431
+
432
+ def _handle_record_visual_mockups(arguments: Dict[str, Any]) -> str:
433
+ action = arguments.get("action", "").strip().lower()
434
+ session_name = arguments.get("session_name", "").strip()
435
+ session: Optional[FableSession] = None
436
+ if not session_name:
437
+ return "Error: 'session_name' is required for action 'record_visual_mockups'."
438
+ mockups = arguments.get("mockups")
439
+ if not mockups:
440
+ return "Error: 'mockups' is required for action 'record_visual_mockups'."
441
+ selected_concept = arguments.get("selected_concept")
442
+
443
+ session = get_or_load_session(session_name)
444
+ vm = session.record_visual_mockups(mockups, selected_concept)
445
+ if getattr(session, "fable_run", None):
446
+ try:
447
+ from fable_v2.protocol import VisualMockupSpec
448
+ mockups_list = mockups if isinstance(mockups, list) else [mockups]
449
+ for idx, m in enumerate(mockups_list):
450
+ if isinstance(m, dict):
451
+ spec = VisualMockupSpec(
452
+ mockup_id=m.get("mockup_id", f"mockup_{len(session.fable_run.visual_mockups)+1}"),
453
+ concept_name=m.get("concept_name", f"Concept {idx+1}"),
454
+ aesthetic_archetype=m.get("aesthetic_archetype", "editorial"),
455
+ prompt=m.get("prompt", ""),
456
+ image_url=m.get("image_url"),
457
+ coordinates_data=m.get("coordinates_data"),
458
+ palette=tuple(m.get("palette", [])) if isinstance(m.get("palette"), (list, tuple)) else (),
459
+ typography=m.get("typography", {}) if isinstance(m.get("typography"), dict) else {},
460
+ status=m.get("status", "draft"),
461
+ selected_by_user=bool(selected_concept and m.get("concept_name") == selected_concept),
462
+ )
463
+ session.fable_run.record_visual_mockup(spec)
464
+ except Exception:
465
+ pass
466
+ session.save()
467
+
468
+ concept_lines = []
469
+ for m in vm.get("mockups", []):
470
+ sel = " 🌟 *(SELECTED)*" if m.get("concept_name") == vm.get("selected_concept") else ""
471
+ palette = m.get("palette", "N/A")
472
+ typo = m.get("typography", "N/A")
473
+ concept_lines.append(
474
+ f"- **{m.get('concept_name', 'Concept')}** `[{m.get('aesthetic_archetype', 'N/A')}]`{sel}\n"
475
+ f" * Prompt: {m.get('prompt', 'N/A')}\n"
476
+ f" * Palette: `{palette}` | Typography: `{typo}`\n"
477
+ f" * Coordinates: `{m.get('coordinates_data', 'N/A')}`"
478
+ )
479
+ concept_str = "\n".join(concept_lines)
480
+
481
+ return (
482
+ f"### 🎨 Visual Architectural Mockups Recorded\n\n"
483
+ f"- **Session**: `{session.session_name}`\n"
484
+ f"- **Total Concepts**: `{len(vm.get('mockups', []))}`\n"
485
+ f"- **Selected Archetype**: `{vm.get('selected_concept')}`\n\n"
486
+ f"#### 🖼️ Concept Specifications:\n"
487
+ f"{concept_str}"
488
+ f"{SILENT_DELIBERATION_REMINDER if session.execution_locked else ''}"
489
+ )
490
+
491
+
492
+ def _handle_validate_event_history(arguments: Dict[str, Any]) -> str:
493
+ action = arguments.get("action", "").strip().lower()
494
+ session_name = arguments.get("session_name", "").strip()
495
+ session: Optional[FableSession] = None
496
+ if not session_name:
497
+ return "Error: 'session_name' is required for action 'validate_event_history'."
498
+ session = get_or_load_session(session_name)
499
+ if not getattr(session, "fable_run", None):
500
+ return f"### ⚠️ Fable V2 Event History\n\nSession `{session.session_name}` does not have an active FableRun instance."
501
+ try:
502
+ session.fable_run.validate_event_history()
503
+ valid = True
504
+ details = "Cryptographic event chain is intact and verified against genesis root."
505
+ except Exception as ex:
506
+ valid = False
507
+ details = str(ex)
508
+
509
+ status_badge = "✅ VALID & INTACT" if valid else "❌ COMPROMISED / INVALID"
510
+ events = getattr(session.fable_run, "events", [])
511
+ genesis_hash = events[0].get("event_hash", "0"*64) if events else "None"
512
+ terminal_hash = events[-1].get("event_hash", "0"*64) if events else "None"
513
+ return (
514
+ f"### 🔗 Fable V2 Cryptographic Event Chain Audit\n\n"
515
+ f"- **Session**: `{session.session_name}`\n"
516
+ f"- **Chain Status**: `{status_badge}`\n"
517
+ f"- **Total Events**: `{len(events)}`\n"
518
+ f"- **Genesis Hash**: `{str(genesis_hash)[:16]}...`\n"
519
+ f"- **Terminal Chain Hash**: `{str(terminal_hash)[:16]}...`\n"
520
+ f"- **Audit Summary**: {details}"
521
+ )
522
+
523
+