the-grid-cc 1.1.0
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.
- package/LICENSE +21 -0
- package/README.md +206 -0
- package/agents/grid-executor.md +62 -0
- package/agents/grid-guard.md +72 -0
- package/agents/grid-planner.md +70 -0
- package/agents/grid-recognizer.md +100 -0
- package/bin/install.js +276 -0
- package/cli/__init__.py +7 -0
- package/cli/main.py +385 -0
- package/commands/grid/VERSION +1 -0
- package/commands/grid/help.md +54 -0
- package/commands/grid/init.md +51 -0
- package/commands/grid/mcp.md +159 -0
- package/commands/grid.md +12 -0
- package/config/grid.yaml +46 -0
- package/core/__init__.py +45 -0
- package/core/block.py +207 -0
- package/core/cluster.py +228 -0
- package/core/disc.py +254 -0
- package/core/energy.py +267 -0
- package/core/grid.py +326 -0
- package/core/io_tower.py +242 -0
- package/core/program.py +268 -0
- package/core/recognizer.py +294 -0
- package/core/thread.py +180 -0
- package/package.json +37 -0
- package/templates/__init__.py +14 -0
- package/templates/status.py +223 -0
- package/templates/welcome.py +101 -0
package/core/energy.py
ADDED
|
@@ -0,0 +1,267 @@
|
|
|
1
|
+
"""
|
|
2
|
+
Energy Management - Token/budget tracking for The Grid.
|
|
3
|
+
|
|
4
|
+
"Programs need Energy to run on The Grid."
|
|
5
|
+
"""
|
|
6
|
+
|
|
7
|
+
from dataclasses import dataclass, field
|
|
8
|
+
from datetime import datetime
|
|
9
|
+
from typing import Optional
|
|
10
|
+
from enum import Enum
|
|
11
|
+
import threading
|
|
12
|
+
|
|
13
|
+
|
|
14
|
+
class EnergyLevel(Enum):
|
|
15
|
+
"""Energy level thresholds."""
|
|
16
|
+
FULL = "full" # > 80%
|
|
17
|
+
NORMAL = "normal" # 40-80%
|
|
18
|
+
LOW = "low" # 10-40%
|
|
19
|
+
CRITICAL = "critical" # < 10%
|
|
20
|
+
|
|
21
|
+
|
|
22
|
+
@dataclass
|
|
23
|
+
class EnergyTransaction:
|
|
24
|
+
"""Record of energy consumption or allocation."""
|
|
25
|
+
timestamp: datetime
|
|
26
|
+
entity_id: str
|
|
27
|
+
entity_type: str # grid, cluster, block, thread, program
|
|
28
|
+
amount: int
|
|
29
|
+
action: str # allocated, consumed, returned
|
|
30
|
+
balance_after: int
|
|
31
|
+
description: str = ""
|
|
32
|
+
|
|
33
|
+
|
|
34
|
+
class EnergyPool:
|
|
35
|
+
"""An energy pool for a Grid entity."""
|
|
36
|
+
|
|
37
|
+
def __init__(self, budget: int, name: str, parent: Optional["EnergyPool"] = None):
|
|
38
|
+
self.budget = budget
|
|
39
|
+
self.name = name
|
|
40
|
+
self.parent = parent
|
|
41
|
+
self.consumed = 0
|
|
42
|
+
self.allocated_to_children = 0
|
|
43
|
+
self.transactions: list[EnergyTransaction] = []
|
|
44
|
+
self._lock = threading.Lock()
|
|
45
|
+
|
|
46
|
+
@property
|
|
47
|
+
def available(self) -> int:
|
|
48
|
+
"""Get available energy (not consumed or allocated to children)."""
|
|
49
|
+
return self.budget - self.consumed - self.allocated_to_children
|
|
50
|
+
|
|
51
|
+
@property
|
|
52
|
+
def remaining(self) -> int:
|
|
53
|
+
"""Get remaining energy (budget minus consumed)."""
|
|
54
|
+
return self.budget - self.consumed
|
|
55
|
+
|
|
56
|
+
@property
|
|
57
|
+
def level(self) -> EnergyLevel:
|
|
58
|
+
"""Get current energy level."""
|
|
59
|
+
pct = self.percentage
|
|
60
|
+
if pct > 80:
|
|
61
|
+
return EnergyLevel.FULL
|
|
62
|
+
elif pct > 40:
|
|
63
|
+
return EnergyLevel.NORMAL
|
|
64
|
+
elif pct > 10:
|
|
65
|
+
return EnergyLevel.LOW
|
|
66
|
+
else:
|
|
67
|
+
return EnergyLevel.CRITICAL
|
|
68
|
+
|
|
69
|
+
@property
|
|
70
|
+
def percentage(self) -> float:
|
|
71
|
+
"""Get energy as percentage of budget."""
|
|
72
|
+
if self.budget == 0:
|
|
73
|
+
return 0.0
|
|
74
|
+
return (self.remaining / self.budget) * 100
|
|
75
|
+
|
|
76
|
+
def consume(self, amount: int, entity_id: str, description: str = "") -> bool:
|
|
77
|
+
"""Consume energy from this pool."""
|
|
78
|
+
with self._lock:
|
|
79
|
+
if amount > self.available:
|
|
80
|
+
return False
|
|
81
|
+
self.consumed += amount
|
|
82
|
+
self._record_transaction(
|
|
83
|
+
entity_id=entity_id,
|
|
84
|
+
entity_type="consumer",
|
|
85
|
+
amount=amount,
|
|
86
|
+
action="consumed",
|
|
87
|
+
description=description,
|
|
88
|
+
)
|
|
89
|
+
return True
|
|
90
|
+
|
|
91
|
+
def allocate_to_child(self, amount: int, child_name: str) -> Optional["EnergyPool"]:
|
|
92
|
+
"""Allocate energy to a child pool."""
|
|
93
|
+
with self._lock:
|
|
94
|
+
if amount > self.available:
|
|
95
|
+
return None
|
|
96
|
+
self.allocated_to_children += amount
|
|
97
|
+
child_pool = EnergyPool(budget=amount, name=child_name, parent=self)
|
|
98
|
+
self._record_transaction(
|
|
99
|
+
entity_id=child_name,
|
|
100
|
+
entity_type="child_pool",
|
|
101
|
+
amount=amount,
|
|
102
|
+
action="allocated",
|
|
103
|
+
description=f"Allocated to {child_name}",
|
|
104
|
+
)
|
|
105
|
+
return child_pool
|
|
106
|
+
|
|
107
|
+
def return_unused(self) -> int:
|
|
108
|
+
"""Return unused energy to parent pool."""
|
|
109
|
+
if not self.parent:
|
|
110
|
+
return 0
|
|
111
|
+
with self._lock:
|
|
112
|
+
unused = self.available
|
|
113
|
+
if unused > 0:
|
|
114
|
+
self.parent.allocated_to_children -= unused
|
|
115
|
+
self.budget -= unused
|
|
116
|
+
self._record_transaction(
|
|
117
|
+
entity_id=self.name,
|
|
118
|
+
entity_type="pool",
|
|
119
|
+
amount=unused,
|
|
120
|
+
action="returned",
|
|
121
|
+
description=f"Returned to {self.parent.name}",
|
|
122
|
+
)
|
|
123
|
+
return unused
|
|
124
|
+
|
|
125
|
+
def _record_transaction(
|
|
126
|
+
self,
|
|
127
|
+
entity_id: str,
|
|
128
|
+
entity_type: str,
|
|
129
|
+
amount: int,
|
|
130
|
+
action: str,
|
|
131
|
+
description: str = "",
|
|
132
|
+
) -> None:
|
|
133
|
+
"""Record an energy transaction."""
|
|
134
|
+
self.transactions.append(EnergyTransaction(
|
|
135
|
+
timestamp=datetime.now(),
|
|
136
|
+
entity_id=entity_id,
|
|
137
|
+
entity_type=entity_type,
|
|
138
|
+
amount=amount,
|
|
139
|
+
action=action,
|
|
140
|
+
balance_after=self.remaining,
|
|
141
|
+
description=description,
|
|
142
|
+
))
|
|
143
|
+
|
|
144
|
+
def get_bar(self, width: int = 20) -> str:
|
|
145
|
+
"""Get a visual energy bar."""
|
|
146
|
+
filled = int((self.percentage / 100) * width)
|
|
147
|
+
empty = width - filled
|
|
148
|
+
return "█" * filled + "░" * empty
|
|
149
|
+
|
|
150
|
+
def summary(self) -> str:
|
|
151
|
+
"""Get energy summary."""
|
|
152
|
+
return (
|
|
153
|
+
f"{self.name}: {self.get_bar()} "
|
|
154
|
+
f"{self.remaining:,}/{self.budget:,} ({self.percentage:.0f}%)"
|
|
155
|
+
)
|
|
156
|
+
|
|
157
|
+
|
|
158
|
+
class EnergyManager:
|
|
159
|
+
"""
|
|
160
|
+
Manages energy (token budgets) across The Grid.
|
|
161
|
+
|
|
162
|
+
Energy flows:
|
|
163
|
+
Grid → Cluster → Block → Thread/Program
|
|
164
|
+
|
|
165
|
+
Each level can allocate energy to children but cannot
|
|
166
|
+
exceed its own budget.
|
|
167
|
+
"""
|
|
168
|
+
|
|
169
|
+
def __init__(
|
|
170
|
+
self,
|
|
171
|
+
grid_budget: int = 10000,
|
|
172
|
+
cluster_budget: int = 2000,
|
|
173
|
+
block_budget: int = 500,
|
|
174
|
+
thread_budget: int = 100,
|
|
175
|
+
spawn_cost: int = 100,
|
|
176
|
+
low_threshold: float = 0.1,
|
|
177
|
+
):
|
|
178
|
+
self.grid_budget = grid_budget
|
|
179
|
+
self.cluster_budget = cluster_budget
|
|
180
|
+
self.block_budget = block_budget
|
|
181
|
+
self.thread_budget = thread_budget
|
|
182
|
+
self.spawn_cost = spawn_cost
|
|
183
|
+
self.low_threshold = low_threshold
|
|
184
|
+
|
|
185
|
+
# Root energy pool
|
|
186
|
+
self.grid_pool = EnergyPool(budget=grid_budget, name="Grid")
|
|
187
|
+
|
|
188
|
+
# Track all pools
|
|
189
|
+
self.pools: dict[str, EnergyPool] = {"grid": self.grid_pool}
|
|
190
|
+
|
|
191
|
+
def create_cluster_pool(self, cluster_id: str) -> Optional[EnergyPool]:
|
|
192
|
+
"""Create an energy pool for a new Cluster."""
|
|
193
|
+
pool = self.grid_pool.allocate_to_child(
|
|
194
|
+
amount=min(self.cluster_budget, self.grid_pool.available),
|
|
195
|
+
child_name=f"cluster:{cluster_id}",
|
|
196
|
+
)
|
|
197
|
+
if pool:
|
|
198
|
+
self.pools[f"cluster:{cluster_id}"] = pool
|
|
199
|
+
return pool
|
|
200
|
+
|
|
201
|
+
def create_block_pool(self, cluster_id: str, block_id: str) -> Optional[EnergyPool]:
|
|
202
|
+
"""Create an energy pool for a new Block."""
|
|
203
|
+
cluster_pool = self.pools.get(f"cluster:{cluster_id}")
|
|
204
|
+
if not cluster_pool:
|
|
205
|
+
return None
|
|
206
|
+
pool = cluster_pool.allocate_to_child(
|
|
207
|
+
amount=min(self.block_budget, cluster_pool.available),
|
|
208
|
+
child_name=f"block:{block_id}",
|
|
209
|
+
)
|
|
210
|
+
if pool:
|
|
211
|
+
self.pools[f"block:{block_id}"] = pool
|
|
212
|
+
return pool
|
|
213
|
+
|
|
214
|
+
def create_thread_pool(self, block_id: str, thread_id: str) -> Optional[EnergyPool]:
|
|
215
|
+
"""Create an energy pool for a new Thread."""
|
|
216
|
+
block_pool = self.pools.get(f"block:{block_id}")
|
|
217
|
+
if not block_pool:
|
|
218
|
+
return None
|
|
219
|
+
pool = block_pool.allocate_to_child(
|
|
220
|
+
amount=min(self.thread_budget, block_pool.available),
|
|
221
|
+
child_name=f"thread:{thread_id}",
|
|
222
|
+
)
|
|
223
|
+
if pool:
|
|
224
|
+
self.pools[f"thread:{thread_id}"] = pool
|
|
225
|
+
return pool
|
|
226
|
+
|
|
227
|
+
def consume(self, pool_id: str, amount: int, description: str = "") -> bool:
|
|
228
|
+
"""Consume energy from a pool."""
|
|
229
|
+
pool = self.pools.get(pool_id)
|
|
230
|
+
if not pool:
|
|
231
|
+
return False
|
|
232
|
+
return pool.consume(amount, pool_id, description)
|
|
233
|
+
|
|
234
|
+
def get_pool(self, pool_id: str) -> Optional[EnergyPool]:
|
|
235
|
+
"""Get a pool by ID."""
|
|
236
|
+
return self.pools.get(pool_id)
|
|
237
|
+
|
|
238
|
+
def is_low(self, pool_id: str) -> bool:
|
|
239
|
+
"""Check if a pool is at low energy."""
|
|
240
|
+
pool = self.pools.get(pool_id)
|
|
241
|
+
if not pool:
|
|
242
|
+
return True
|
|
243
|
+
return pool.percentage <= (self.low_threshold * 100)
|
|
244
|
+
|
|
245
|
+
def grid_summary(self) -> str:
|
|
246
|
+
"""Get full grid energy summary."""
|
|
247
|
+
lines = ["ENERGY FLOW", "═" * 40]
|
|
248
|
+
lines.append(self.grid_pool.summary())
|
|
249
|
+
|
|
250
|
+
# Show cluster pools
|
|
251
|
+
for pool_id, pool in self.pools.items():
|
|
252
|
+
if pool_id.startswith("cluster:"):
|
|
253
|
+
lines.append(f" └─ {pool.summary()}")
|
|
254
|
+
# Show block pools under this cluster
|
|
255
|
+
for block_id, block_pool in self.pools.items():
|
|
256
|
+
if block_id.startswith("block:") and block_pool.parent == pool:
|
|
257
|
+
lines.append(f" ├─ {block_pool.summary()}")
|
|
258
|
+
|
|
259
|
+
return "\n".join(lines)
|
|
260
|
+
|
|
261
|
+
def total_consumed(self) -> int:
|
|
262
|
+
"""Get total energy consumed across all pools."""
|
|
263
|
+
return sum(pool.consumed for pool in self.pools.values())
|
|
264
|
+
|
|
265
|
+
def total_remaining(self) -> int:
|
|
266
|
+
"""Get total remaining energy in grid."""
|
|
267
|
+
return self.grid_pool.remaining
|
package/core/grid.py
ADDED
|
@@ -0,0 +1,326 @@
|
|
|
1
|
+
"""
|
|
2
|
+
The Grid - Main orchestration class.
|
|
3
|
+
|
|
4
|
+
"The Grid. A digital frontier. I tried to picture clusters of information
|
|
5
|
+
as they moved through the computer."
|
|
6
|
+
"""
|
|
7
|
+
|
|
8
|
+
from dataclasses import dataclass, field
|
|
9
|
+
from datetime import datetime
|
|
10
|
+
from pathlib import Path
|
|
11
|
+
from typing import Optional, Any
|
|
12
|
+
from enum import Enum
|
|
13
|
+
import uuid
|
|
14
|
+
import yaml
|
|
15
|
+
import json
|
|
16
|
+
|
|
17
|
+
from .cluster import Cluster, ClusterStatus
|
|
18
|
+
from .block import Block, BlockStatus
|
|
19
|
+
from .thread import Thread, ThreadStatus
|
|
20
|
+
from .program import Program, ProgramStatus
|
|
21
|
+
from .recognizer import Recognizer, RecognizerType
|
|
22
|
+
from .disc import IdentityDisc
|
|
23
|
+
from .energy import EnergyManager, EnergyPool
|
|
24
|
+
from .io_tower import IOTower, CheckpointReason, CheckpointContext
|
|
25
|
+
|
|
26
|
+
|
|
27
|
+
class GridStatus(Enum):
|
|
28
|
+
"""Status of The Grid."""
|
|
29
|
+
INITIALIZING = "initializing"
|
|
30
|
+
READY = "ready"
|
|
31
|
+
RUNNING = "running"
|
|
32
|
+
PAUSED = "paused"
|
|
33
|
+
COMPLETED = "completed"
|
|
34
|
+
FAILED = "failed"
|
|
35
|
+
|
|
36
|
+
|
|
37
|
+
@dataclass
|
|
38
|
+
class GridConfig:
|
|
39
|
+
"""Configuration for The Grid."""
|
|
40
|
+
# Fission control
|
|
41
|
+
max_depth: int = 4
|
|
42
|
+
max_concurrent: int = 8
|
|
43
|
+
energy_per_spawn: int = 100
|
|
44
|
+
|
|
45
|
+
# Energy budgets
|
|
46
|
+
grid_budget: int = 10000
|
|
47
|
+
cluster_budget: int = 2000
|
|
48
|
+
block_budget: int = 500
|
|
49
|
+
thread_budget: int = 100
|
|
50
|
+
low_energy_threshold: float = 0.1
|
|
51
|
+
|
|
52
|
+
# I/O Tower
|
|
53
|
+
require_at_depth: int = 3
|
|
54
|
+
require_before_commit: bool = True
|
|
55
|
+
require_on_error: bool = True
|
|
56
|
+
timeout_seconds: int = 300
|
|
57
|
+
|
|
58
|
+
# Display
|
|
59
|
+
tron_aesthetics: bool = True
|
|
60
|
+
|
|
61
|
+
@classmethod
|
|
62
|
+
def from_yaml(cls, path: Path) -> "GridConfig":
|
|
63
|
+
"""Load config from YAML file."""
|
|
64
|
+
with open(path) as f:
|
|
65
|
+
data = yaml.safe_load(f)
|
|
66
|
+
|
|
67
|
+
fission = data.get("fission", {})
|
|
68
|
+
energy = data.get("energy", {})
|
|
69
|
+
io = data.get("io_towers", {})
|
|
70
|
+
display = data.get("display", {})
|
|
71
|
+
|
|
72
|
+
return cls(
|
|
73
|
+
max_depth=fission.get("max_depth", 4),
|
|
74
|
+
max_concurrent=fission.get("max_concurrent", 8),
|
|
75
|
+
energy_per_spawn=fission.get("energy_per_spawn", 100),
|
|
76
|
+
grid_budget=energy.get("grid_budget", 10000),
|
|
77
|
+
cluster_budget=energy.get("cluster_budget", 2000),
|
|
78
|
+
block_budget=energy.get("block_budget", 500),
|
|
79
|
+
thread_budget=energy.get("thread_budget", 100),
|
|
80
|
+
low_energy_threshold=energy.get("low_energy_threshold", 0.1),
|
|
81
|
+
require_at_depth=io.get("require_at_depth", 3),
|
|
82
|
+
require_before_commit=io.get("require_before_commit", True),
|
|
83
|
+
require_on_error=io.get("require_on_error", True),
|
|
84
|
+
timeout_seconds=io.get("timeout_seconds", 300),
|
|
85
|
+
tron_aesthetics=display.get("tron_aesthetics", True),
|
|
86
|
+
)
|
|
87
|
+
|
|
88
|
+
|
|
89
|
+
class Grid:
|
|
90
|
+
"""
|
|
91
|
+
The Grid - Main orchestration class.
|
|
92
|
+
|
|
93
|
+
The Grid is the digital frontier where Clusters, Blocks, Threads,
|
|
94
|
+
and Programs execute. It manages the entire execution hierarchy
|
|
95
|
+
and coordinates energy flow.
|
|
96
|
+
"""
|
|
97
|
+
|
|
98
|
+
def __init__(
|
|
99
|
+
self,
|
|
100
|
+
name: str = "The Grid",
|
|
101
|
+
config: GridConfig = None,
|
|
102
|
+
config_path: Path = None,
|
|
103
|
+
):
|
|
104
|
+
self.id = str(uuid.uuid4())[:8]
|
|
105
|
+
self.name = name
|
|
106
|
+
self.status = GridStatus.INITIALIZING
|
|
107
|
+
self.created_at = datetime.now()
|
|
108
|
+
|
|
109
|
+
# Load configuration
|
|
110
|
+
if config:
|
|
111
|
+
self.config = config
|
|
112
|
+
elif config_path and config_path.exists():
|
|
113
|
+
self.config = GridConfig.from_yaml(config_path)
|
|
114
|
+
else:
|
|
115
|
+
self.config = GridConfig()
|
|
116
|
+
|
|
117
|
+
# Energy management
|
|
118
|
+
self.energy = EnergyManager(
|
|
119
|
+
grid_budget=self.config.grid_budget,
|
|
120
|
+
cluster_budget=self.config.cluster_budget,
|
|
121
|
+
block_budget=self.config.block_budget,
|
|
122
|
+
thread_budget=self.config.thread_budget,
|
|
123
|
+
spawn_cost=self.config.energy_per_spawn,
|
|
124
|
+
low_threshold=self.config.low_energy_threshold,
|
|
125
|
+
)
|
|
126
|
+
|
|
127
|
+
# I/O Tower (human checkpoints)
|
|
128
|
+
self.io_tower = IOTower(
|
|
129
|
+
require_at_depth=self.config.require_at_depth,
|
|
130
|
+
require_before_commit=self.config.require_before_commit,
|
|
131
|
+
require_on_error=self.config.require_on_error,
|
|
132
|
+
timeout_seconds=self.config.timeout_seconds,
|
|
133
|
+
)
|
|
134
|
+
|
|
135
|
+
# Clusters
|
|
136
|
+
self.clusters: dict[str, Cluster] = {}
|
|
137
|
+
|
|
138
|
+
# Active Programs (for tracking concurrent execution)
|
|
139
|
+
self.active_programs: dict[str, Program] = {}
|
|
140
|
+
|
|
141
|
+
# Statistics
|
|
142
|
+
self.cycles_completed = 0
|
|
143
|
+
self.programs_spawned = 0
|
|
144
|
+
self.programs_derezzed = 0
|
|
145
|
+
|
|
146
|
+
self.status = GridStatus.READY
|
|
147
|
+
|
|
148
|
+
def create_cluster(
|
|
149
|
+
self,
|
|
150
|
+
name: str,
|
|
151
|
+
purpose: str,
|
|
152
|
+
description: str = "",
|
|
153
|
+
) -> Cluster:
|
|
154
|
+
"""Create a new Cluster on The Grid."""
|
|
155
|
+
cluster = Cluster(
|
|
156
|
+
name=name,
|
|
157
|
+
purpose=purpose,
|
|
158
|
+
description=description,
|
|
159
|
+
)
|
|
160
|
+
|
|
161
|
+
# Allocate energy
|
|
162
|
+
cluster.energy_pool = self.energy.create_cluster_pool(cluster.id)
|
|
163
|
+
|
|
164
|
+
self.clusters[cluster.id] = cluster
|
|
165
|
+
return cluster
|
|
166
|
+
|
|
167
|
+
def get_cluster(self, cluster_id: str) -> Optional[Cluster]:
|
|
168
|
+
"""Get a Cluster by ID."""
|
|
169
|
+
return self.clusters.get(cluster_id)
|
|
170
|
+
|
|
171
|
+
def get_cluster_by_name(self, name: str) -> Optional[Cluster]:
|
|
172
|
+
"""Get a Cluster by name."""
|
|
173
|
+
for cluster in self.clusters.values():
|
|
174
|
+
if cluster.name == name:
|
|
175
|
+
return cluster
|
|
176
|
+
return None
|
|
177
|
+
|
|
178
|
+
def spawn_program(
|
|
179
|
+
self,
|
|
180
|
+
name: str,
|
|
181
|
+
purpose: str,
|
|
182
|
+
parent: Program = None,
|
|
183
|
+
energy_budget: int = None,
|
|
184
|
+
) -> Optional[Program]:
|
|
185
|
+
"""Spawn a new Program on The Grid."""
|
|
186
|
+
if energy_budget is None:
|
|
187
|
+
energy_budget = self.config.energy_per_spawn
|
|
188
|
+
|
|
189
|
+
# Check concurrent limit
|
|
190
|
+
if len(self.active_programs) >= self.config.max_concurrent:
|
|
191
|
+
return None
|
|
192
|
+
|
|
193
|
+
# Check depth limit if parent exists
|
|
194
|
+
depth = 0 if parent is None else parent.depth + 1
|
|
195
|
+
if depth > self.config.max_depth:
|
|
196
|
+
return None
|
|
197
|
+
|
|
198
|
+
# Check if I/O Tower checkpoint needed
|
|
199
|
+
if self.io_tower.should_checkpoint(CheckpointReason.FISSION_DEPTH, depth):
|
|
200
|
+
context = CheckpointContext(
|
|
201
|
+
reason=CheckpointReason.FISSION_DEPTH,
|
|
202
|
+
program_id=parent.id if parent else "grid",
|
|
203
|
+
program_name=parent.name if parent else "Grid",
|
|
204
|
+
depth=depth,
|
|
205
|
+
energy_remaining=parent.disc.energy_remaining() if parent else self.energy.total_remaining(),
|
|
206
|
+
energy_allocated=parent.disc.energy_allocated if parent else self.config.grid_budget,
|
|
207
|
+
pending_actions=[f"Spawn Program: {name}"],
|
|
208
|
+
details={"purpose": purpose, "energy_cost": energy_budget},
|
|
209
|
+
)
|
|
210
|
+
# In actual use, this would pause for user input
|
|
211
|
+
# For now, we continue (checkpoint is logged)
|
|
212
|
+
self.io_tower.checkpoint(context)
|
|
213
|
+
|
|
214
|
+
program = Program(
|
|
215
|
+
name=name,
|
|
216
|
+
purpose=purpose,
|
|
217
|
+
parent=parent,
|
|
218
|
+
energy_budget=energy_budget,
|
|
219
|
+
)
|
|
220
|
+
|
|
221
|
+
self.active_programs[program.id] = program
|
|
222
|
+
self.programs_spawned += 1
|
|
223
|
+
|
|
224
|
+
return program
|
|
225
|
+
|
|
226
|
+
def derez_program(self, program: Program) -> None:
|
|
227
|
+
"""Derez (terminate) a Program."""
|
|
228
|
+
program.derez()
|
|
229
|
+
if program.id in self.active_programs:
|
|
230
|
+
del self.active_programs[program.id]
|
|
231
|
+
self.programs_derezzed += 1
|
|
232
|
+
|
|
233
|
+
def start(self) -> None:
|
|
234
|
+
"""Start Grid execution."""
|
|
235
|
+
self.status = GridStatus.RUNNING
|
|
236
|
+
|
|
237
|
+
# Start all pending clusters
|
|
238
|
+
for cluster in self.clusters.values():
|
|
239
|
+
if cluster.status == ClusterStatus.PENDING:
|
|
240
|
+
cluster.start()
|
|
241
|
+
|
|
242
|
+
def pause(self) -> None:
|
|
243
|
+
"""Pause Grid execution."""
|
|
244
|
+
self.status = GridStatus.PAUSED
|
|
245
|
+
|
|
246
|
+
def resume(self) -> None:
|
|
247
|
+
"""Resume Grid execution."""
|
|
248
|
+
if self.status == GridStatus.PAUSED:
|
|
249
|
+
self.status = GridStatus.RUNNING
|
|
250
|
+
|
|
251
|
+
def complete_cycle(self) -> None:
|
|
252
|
+
"""Complete a cycle of execution."""
|
|
253
|
+
self.cycles_completed += 1
|
|
254
|
+
|
|
255
|
+
def is_complete(self) -> bool:
|
|
256
|
+
"""Check if all work on The Grid is complete."""
|
|
257
|
+
return all(
|
|
258
|
+
c.status in (ClusterStatus.COMPLETED, ClusterStatus.DEREZZED)
|
|
259
|
+
for c in self.clusters.values()
|
|
260
|
+
)
|
|
261
|
+
|
|
262
|
+
def get_status_summary(self) -> dict:
|
|
263
|
+
"""Get Grid status summary."""
|
|
264
|
+
return {
|
|
265
|
+
"id": self.id,
|
|
266
|
+
"name": self.name,
|
|
267
|
+
"status": self.status.value,
|
|
268
|
+
"clusters": len(self.clusters),
|
|
269
|
+
"active_programs": len(self.active_programs),
|
|
270
|
+
"programs_spawned": self.programs_spawned,
|
|
271
|
+
"programs_derezzed": self.programs_derezzed,
|
|
272
|
+
"cycles_completed": self.cycles_completed,
|
|
273
|
+
"energy_remaining": self.energy.total_remaining(),
|
|
274
|
+
"energy_consumed": self.energy.total_consumed(),
|
|
275
|
+
}
|
|
276
|
+
|
|
277
|
+
def energy_percentage(self) -> float:
|
|
278
|
+
"""Get Grid energy as percentage."""
|
|
279
|
+
return self.energy.grid_pool.percentage
|
|
280
|
+
|
|
281
|
+
def get_energy_bar(self, width: int = 20) -> str:
|
|
282
|
+
"""Get visual energy bar."""
|
|
283
|
+
return self.energy.grid_pool.get_bar(width)
|
|
284
|
+
|
|
285
|
+
def render_status(self) -> str:
|
|
286
|
+
"""Render Grid status display."""
|
|
287
|
+
lines = [
|
|
288
|
+
"╔" + "═" * 77 + "╗",
|
|
289
|
+
"║" + " " * 77 + "║",
|
|
290
|
+
f"║ GRID STATUS {self.get_energy_bar()} {self.energy_percentage():.0f}% Energy" + " " * 22 + "║",
|
|
291
|
+
f"║ Active Programs {len(self.active_programs):<54}║",
|
|
292
|
+
f"║ Clusters {len(self.clusters):<54}║",
|
|
293
|
+
f"║ Cycles completed {self.cycles_completed:<54}║",
|
|
294
|
+
"║" + " " * 77 + "║",
|
|
295
|
+
]
|
|
296
|
+
return "\n".join(lines)
|
|
297
|
+
|
|
298
|
+
def to_dict(self) -> dict:
|
|
299
|
+
"""Serialize Grid to dictionary."""
|
|
300
|
+
return {
|
|
301
|
+
"id": self.id,
|
|
302
|
+
"name": self.name,
|
|
303
|
+
"status": self.status.value,
|
|
304
|
+
"created_at": self.created_at.isoformat(),
|
|
305
|
+
"config": {
|
|
306
|
+
"max_depth": self.config.max_depth,
|
|
307
|
+
"max_concurrent": self.config.max_concurrent,
|
|
308
|
+
"grid_budget": self.config.grid_budget,
|
|
309
|
+
},
|
|
310
|
+
"clusters": [c.to_dict() for c in self.clusters.values()],
|
|
311
|
+
"active_programs": [p.to_dict() for p in self.active_programs.values()],
|
|
312
|
+
"statistics": {
|
|
313
|
+
"cycles_completed": self.cycles_completed,
|
|
314
|
+
"programs_spawned": self.programs_spawned,
|
|
315
|
+
"programs_derezzed": self.programs_derezzed,
|
|
316
|
+
"energy_remaining": self.energy.total_remaining(),
|
|
317
|
+
"energy_consumed": self.energy.total_consumed(),
|
|
318
|
+
},
|
|
319
|
+
}
|
|
320
|
+
|
|
321
|
+
def to_json(self) -> str:
|
|
322
|
+
"""Serialize Grid to JSON."""
|
|
323
|
+
return json.dumps(self.to_dict(), indent=2)
|
|
324
|
+
|
|
325
|
+
def __repr__(self) -> str:
|
|
326
|
+
return f"Grid(id={self.id}, name={self.name}, clusters={len(self.clusters)})"
|