arkaos 2.17.1 → 2.17.4
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/VERSION +1 -1
- package/arka/SKILL.md +9 -67
- package/arka/skills/comfyui/SKILL.md +50 -12
- package/arka/skills/conclave/SKILL.md +43 -141
- package/arka/skills/conclave/references/advisors.md +36 -0
- package/arka/skills/human-writing/SKILL.md +15 -100
- package/arka/skills/human-writing/references/forbidden-patterns.md +32 -0
- package/config/hooks/post-tool-use.sh +72 -0
- package/config/hooks/session-start.sh +16 -0
- package/config/hooks/user-prompt-submit.ps1 +2 -2
- package/config/hooks/user-prompt-submit.sh +102 -26
- package/core/agents/__pycache__/behavior_enforcer.cpython-313.pyc +0 -0
- package/core/agents/__pycache__/dna_registry.cpython-313.pyc +0 -0
- package/core/agents/adapters/__pycache__/disc_adapter.cpython-313.pyc +0 -0
- package/core/agents/adapters/disc_adapter.py +149 -0
- package/core/agents/behavior_enforcer.py +255 -0
- package/core/agents/dna_registry.py +235 -0
- package/core/forge/__init__.py +36 -0
- package/core/forge/__pycache__/__init__.cpython-313.pyc +0 -0
- package/core/forge/__pycache__/orchestrator.cpython-313.pyc +0 -0
- package/core/forge/__pycache__/runtime_dispatcher.cpython-313.pyc +0 -0
- package/core/forge/orchestrator.py +770 -0
- package/core/forge/runtime_dispatcher.py +465 -0
- package/core/governance/__pycache__/quality_api.cpython-313.pyc +0 -0
- package/core/governance/__pycache__/quality_router.cpython-313.pyc +0 -0
- package/core/governance/__pycache__/review_workflow.cpython-313.pyc +0 -0
- package/core/governance/quality_api.py +280 -0
- package/core/governance/quality_router.py +304 -0
- package/core/governance/review_workflow.py +386 -0
- package/core/memory/__pycache__/compressor.cpython-313.pyc +0 -0
- package/core/memory/__pycache__/rehydrator.cpython-313.pyc +0 -0
- package/core/memory/__pycache__/session_store.cpython-313.pyc +0 -0
- package/core/memory/compressor.py +269 -0
- package/core/memory/rehydrator.py +204 -0
- package/core/memory/session_store.py +256 -0
- package/core/runtime/__pycache__/context_compactor.cpython-313.pyc +0 -0
- package/core/runtime/__pycache__/subagent.cpython-313.pyc +0 -0
- package/core/synapse/__init__.py +10 -3
- package/core/synapse/__pycache__/__init__.cpython-313.pyc +0 -0
- package/core/synapse/__pycache__/engine.cpython-313.pyc +0 -0
- package/core/synapse/__pycache__/kb_cache.cpython-313.pyc +0 -0
- package/core/synapse/__pycache__/layers.cpython-313.pyc +0 -0
- package/core/synapse/engine.py +27 -16
- package/core/synapse/kb_cache.py +382 -0
- package/core/synapse/layers.py +253 -50
- package/core/sync/__pycache__/self_healing.cpython-313.pyc +0 -0
- package/core/workflow/__pycache__/announcer.cpython-313.pyc +0 -0
- package/core/workflow/__pycache__/dashboard.cpython-313.pyc +0 -0
- package/core/workflow/__pycache__/enforcer.cpython-313.pyc +0 -0
- package/core/workflow/__pycache__/rules_registry.cpython-313.pyc +0 -0
- package/core/workflow/__pycache__/state.cpython-313.pyc +0 -0
- package/core/workflow/announcer.py +246 -0
- package/core/workflow/dashboard.py +194 -0
- package/core/workflow/enforcer.py +234 -0
- package/core/workflow/recovery.py +196 -0
- package/core/workflow/rules_registry.py +484 -0
- package/core/workflow/session_summary.py +204 -0
- package/core/workflow/state.py +12 -2
- package/departments/dev/SKILL.md +10 -42
- package/departments/dev/skills/agent-design/SKILL.md +6 -26
- package/departments/dev/skills/ci-cd-pipeline/SKILL.md +6 -29
- package/departments/dev/skills/db-schema/SKILL.md +6 -24
- package/departments/dev/skills/dependency-audit/SKILL.md +1 -3
- package/departments/dev/skills/incident/SKILL.md +6 -24
- package/departments/dev/skills/mcp-builder/SKILL.md +4 -17
- package/departments/dev/skills/observability/SKILL.md +6 -29
- package/departments/dev/skills/performance-profiler/SKILL.md +5 -26
- package/departments/dev/skills/rag-architect/SKILL.md +5 -23
- package/departments/dev/skills/release/SKILL.md +4 -17
- package/departments/dev/skills/spec/SKILL.md +47 -148
- package/departments/landing/skills/landing-gen/SKILL.md +3 -15
- package/departments/marketing/skills/cold-email/SKILL.md +5 -17
- package/departments/marketing/skills/programmatic-seo/SKILL.md +6 -21
- package/departments/strategy/skills/board-advisor/SKILL.md +7 -21
- package/package.json +1 -1
- package/pyproject.toml +1 -1
- package/scripts/synapse-bridge.py +27 -3
package/core/synapse/layers.py
CHANGED
|
@@ -25,17 +25,19 @@ from typing import Optional, Any
|
|
|
25
25
|
@dataclass
|
|
26
26
|
class LayerResult:
|
|
27
27
|
"""Result from computing a single layer."""
|
|
28
|
+
|
|
28
29
|
layer_id: str
|
|
29
|
-
tag: str
|
|
30
|
-
content: str
|
|
30
|
+
tag: str # e.g., "[dept:dev]"
|
|
31
|
+
content: str # Full content for this layer
|
|
31
32
|
tokens_est: int # Estimated token count
|
|
32
33
|
compute_ms: int # Time to compute in milliseconds
|
|
33
|
-
cached: bool
|
|
34
|
+
cached: bool # Whether this was served from cache
|
|
34
35
|
|
|
35
36
|
|
|
36
37
|
@dataclass
|
|
37
38
|
class PromptContext:
|
|
38
39
|
"""Input context for layer computation."""
|
|
40
|
+
|
|
39
41
|
user_input: str = ""
|
|
40
42
|
cwd: str = ""
|
|
41
43
|
git_branch: str = ""
|
|
@@ -87,6 +89,7 @@ class Layer(ABC):
|
|
|
87
89
|
|
|
88
90
|
# --- L0: Constitution ---
|
|
89
91
|
|
|
92
|
+
|
|
90
93
|
class ConstitutionLayer(Layer):
|
|
91
94
|
"""L0: Compressed Constitution rules. Highest priority, longest cache."""
|
|
92
95
|
|
|
@@ -168,23 +171,49 @@ class DepartmentLayer(Layer):
|
|
|
168
171
|
text = ctx.user_input.lower()
|
|
169
172
|
|
|
170
173
|
# Check for explicit command prefix first
|
|
171
|
-
|
|
174
|
+
# Use [-\w] to handle hyphenated commands like /arka-do
|
|
175
|
+
prefix_match = re.match(r"^/([-\w]+)\s", text)
|
|
172
176
|
if prefix_match:
|
|
173
177
|
prefix = prefix_match.group(1)
|
|
174
178
|
dept_map = {
|
|
175
|
-
"dev": "dev",
|
|
176
|
-
"
|
|
177
|
-
"
|
|
178
|
-
"
|
|
179
|
-
"
|
|
180
|
-
"
|
|
179
|
+
"dev": "dev",
|
|
180
|
+
"mkt": "marketing",
|
|
181
|
+
"fin": "finance",
|
|
182
|
+
"strat": "strategy",
|
|
183
|
+
"ops": "ops",
|
|
184
|
+
"ecom": "ecom",
|
|
185
|
+
"kb": "kb",
|
|
186
|
+
"brand": "brand",
|
|
187
|
+
"saas": "saas",
|
|
188
|
+
"landing": "landing",
|
|
189
|
+
"community": "community",
|
|
190
|
+
"content": "content",
|
|
191
|
+
"pm": "pm",
|
|
192
|
+
"lead": "lead",
|
|
193
|
+
"sales": "sales",
|
|
194
|
+
"org": "org",
|
|
195
|
+
"do": "orchestrator",
|
|
196
|
+
"arka-do": "orchestrator",
|
|
181
197
|
}
|
|
182
198
|
if prefix in dept_map:
|
|
183
199
|
dept = dept_map[prefix]
|
|
184
200
|
ms = int((time.time() - start) * 1000)
|
|
201
|
+
if dept == "orchestrator":
|
|
202
|
+
return LayerResult(
|
|
203
|
+
layer_id=self.id,
|
|
204
|
+
tag="",
|
|
205
|
+
content="",
|
|
206
|
+
tokens_est=0,
|
|
207
|
+
compute_ms=ms,
|
|
208
|
+
cached=False,
|
|
209
|
+
)
|
|
185
210
|
return LayerResult(
|
|
186
|
-
layer_id=self.id,
|
|
187
|
-
|
|
211
|
+
layer_id=self.id,
|
|
212
|
+
tag=f"[dept:{dept}]",
|
|
213
|
+
content=dept,
|
|
214
|
+
tokens_est=1,
|
|
215
|
+
compute_ms=ms,
|
|
216
|
+
cached=False,
|
|
188
217
|
)
|
|
189
218
|
|
|
190
219
|
# Pattern matching on input text
|
|
@@ -199,13 +228,18 @@ class DepartmentLayer(Layer):
|
|
|
199
228
|
|
|
200
229
|
ms = int((time.time() - start) * 1000)
|
|
201
230
|
return LayerResult(
|
|
202
|
-
layer_id=self.id,
|
|
203
|
-
|
|
231
|
+
layer_id=self.id,
|
|
232
|
+
tag=tag,
|
|
233
|
+
content=dept,
|
|
234
|
+
tokens_est=1,
|
|
235
|
+
compute_ms=ms,
|
|
236
|
+
cached=False,
|
|
204
237
|
)
|
|
205
238
|
|
|
206
239
|
|
|
207
240
|
# --- L2: Agent Context ---
|
|
208
241
|
|
|
242
|
+
|
|
209
243
|
class AgentLayer(Layer):
|
|
210
244
|
"""L2: Active agent profile and recent gotchas."""
|
|
211
245
|
|
|
@@ -234,8 +268,12 @@ class AgentLayer(Layer):
|
|
|
234
268
|
if not agent_id:
|
|
235
269
|
ms = int((time.time() - start) * 1000)
|
|
236
270
|
return LayerResult(
|
|
237
|
-
layer_id=self.id,
|
|
238
|
-
|
|
271
|
+
layer_id=self.id,
|
|
272
|
+
tag="",
|
|
273
|
+
content="",
|
|
274
|
+
tokens_est=0,
|
|
275
|
+
compute_ms=ms,
|
|
276
|
+
cached=False,
|
|
239
277
|
)
|
|
240
278
|
|
|
241
279
|
agent = self._registry.get(agent_id, {})
|
|
@@ -244,13 +282,18 @@ class AgentLayer(Layer):
|
|
|
244
282
|
|
|
245
283
|
ms = int((time.time() - start) * 1000)
|
|
246
284
|
return LayerResult(
|
|
247
|
-
layer_id=self.id,
|
|
248
|
-
|
|
285
|
+
layer_id=self.id,
|
|
286
|
+
tag=tag,
|
|
287
|
+
content=agent_id,
|
|
288
|
+
tokens_est=3,
|
|
289
|
+
compute_ms=ms,
|
|
290
|
+
cached=False,
|
|
249
291
|
)
|
|
250
292
|
|
|
251
293
|
|
|
252
294
|
# --- L3: Project Context ---
|
|
253
295
|
|
|
296
|
+
|
|
254
297
|
class ProjectLayer(Layer):
|
|
255
298
|
"""L3: Active project name and stack."""
|
|
256
299
|
|
|
@@ -281,13 +324,18 @@ class ProjectLayer(Layer):
|
|
|
281
324
|
tag = f"[{' '.join(parts)}]" if parts else ""
|
|
282
325
|
ms = int((time.time() - start) * 1000)
|
|
283
326
|
return LayerResult(
|
|
284
|
-
layer_id=self.id,
|
|
285
|
-
|
|
327
|
+
layer_id=self.id,
|
|
328
|
+
tag=tag,
|
|
329
|
+
content=ctx.project_name or "",
|
|
330
|
+
tokens_est=len(parts),
|
|
331
|
+
compute_ms=ms,
|
|
332
|
+
cached=False,
|
|
286
333
|
)
|
|
287
334
|
|
|
288
335
|
|
|
289
336
|
# --- L4: Git Branch ---
|
|
290
337
|
|
|
338
|
+
|
|
291
339
|
class BranchLayer(Layer):
|
|
292
340
|
"""L4: Current git branch (hidden for main/master/dev)."""
|
|
293
341
|
|
|
@@ -314,13 +362,18 @@ class BranchLayer(Layer):
|
|
|
314
362
|
|
|
315
363
|
ms = int((time.time() - start) * 1000)
|
|
316
364
|
return LayerResult(
|
|
317
|
-
layer_id=self.id,
|
|
318
|
-
|
|
365
|
+
layer_id=self.id,
|
|
366
|
+
tag=tag,
|
|
367
|
+
content=branch,
|
|
368
|
+
tokens_est=1 if tag else 0,
|
|
369
|
+
compute_ms=ms,
|
|
370
|
+
cached=False,
|
|
319
371
|
)
|
|
320
372
|
|
|
321
373
|
|
|
322
374
|
# --- L5: Command Hints ---
|
|
323
375
|
|
|
376
|
+
|
|
324
377
|
class CommandHintsLayer(Layer):
|
|
325
378
|
"""L5: Matching commands from the registry for non-explicit requests."""
|
|
326
379
|
|
|
@@ -347,12 +400,17 @@ class CommandHintsLayer(Layer):
|
|
|
347
400
|
start = time.time()
|
|
348
401
|
text = ctx.user_input.lower()
|
|
349
402
|
|
|
350
|
-
# Skip if already an explicit command
|
|
351
|
-
|
|
403
|
+
# Skip if already an explicit command — EXCEPT /arka-do which needs hints
|
|
404
|
+
# /do-style commands should still get command hints for sub-commands
|
|
405
|
+
if text.startswith("/") and not text.startswith("/arka-do"):
|
|
352
406
|
ms = int((time.time() - start) * 1000)
|
|
353
407
|
return LayerResult(
|
|
354
|
-
layer_id=self.id,
|
|
355
|
-
|
|
408
|
+
layer_id=self.id,
|
|
409
|
+
tag="",
|
|
410
|
+
content="",
|
|
411
|
+
tokens_est=0,
|
|
412
|
+
compute_ms=ms,
|
|
413
|
+
cached=False,
|
|
356
414
|
)
|
|
357
415
|
|
|
358
416
|
# Score commands by keyword match
|
|
@@ -369,13 +427,18 @@ class CommandHintsLayer(Layer):
|
|
|
369
427
|
tags = " ".join(f"[hint:{h}]" for h in hints)
|
|
370
428
|
ms = int((time.time() - start) * 1000)
|
|
371
429
|
return LayerResult(
|
|
372
|
-
layer_id=self.id,
|
|
373
|
-
|
|
430
|
+
layer_id=self.id,
|
|
431
|
+
tag=tags,
|
|
432
|
+
content=" ".join(hints),
|
|
433
|
+
tokens_est=len(hints) * 2,
|
|
434
|
+
compute_ms=ms,
|
|
435
|
+
cached=False,
|
|
374
436
|
)
|
|
375
437
|
|
|
376
438
|
|
|
377
439
|
# --- L6: Quality Gate Status ---
|
|
378
440
|
|
|
441
|
+
|
|
379
442
|
class QualityGateLayer(Layer):
|
|
380
443
|
"""L6: Current quality gate status and recent verdicts."""
|
|
381
444
|
|
|
@@ -397,17 +460,41 @@ class QualityGateLayer(Layer):
|
|
|
397
460
|
|
|
398
461
|
def compute(self, ctx: PromptContext) -> LayerResult:
|
|
399
462
|
start = time.time()
|
|
400
|
-
|
|
463
|
+
try:
|
|
464
|
+
from core.governance.quality_api import list_pending, list_approved
|
|
465
|
+
|
|
466
|
+
pending = list_pending()
|
|
467
|
+
approved = list_approved(limit=3)
|
|
468
|
+
except Exception:
|
|
469
|
+
pending = []
|
|
470
|
+
approved = []
|
|
471
|
+
|
|
472
|
+
parts = []
|
|
473
|
+
if pending:
|
|
474
|
+
parts.append(f"{len(pending)} pending")
|
|
475
|
+
titles = [p.get("title", "")[:30] for p in pending[:2]]
|
|
476
|
+
parts.append(f"pending: {'; '.join(titles)}")
|
|
477
|
+
if approved:
|
|
478
|
+
parts.append(f"recent approved: {len(approved)}")
|
|
479
|
+
|
|
401
480
|
tag = "[qg:active]"
|
|
481
|
+
content = " | ".join(parts) if parts else "active"
|
|
482
|
+
tokens = len(content.split())
|
|
483
|
+
|
|
402
484
|
ms = int((time.time() - start) * 1000)
|
|
403
485
|
return LayerResult(
|
|
404
|
-
layer_id=self.id,
|
|
405
|
-
|
|
486
|
+
layer_id=self.id,
|
|
487
|
+
tag=tag,
|
|
488
|
+
content=content,
|
|
489
|
+
tokens_est=tokens,
|
|
490
|
+
compute_ms=ms,
|
|
491
|
+
cached=False,
|
|
406
492
|
)
|
|
407
493
|
|
|
408
494
|
|
|
409
495
|
# --- L7: Time Signal ---
|
|
410
496
|
|
|
497
|
+
|
|
411
498
|
class TimeLayer(Layer):
|
|
412
499
|
"""L7: Time-of-day signal for context-aware behavior."""
|
|
413
500
|
|
|
@@ -433,6 +520,7 @@ class TimeLayer(Layer):
|
|
|
433
520
|
def compute(self, ctx: PromptContext) -> LayerResult:
|
|
434
521
|
start = time.time()
|
|
435
522
|
import datetime
|
|
523
|
+
|
|
436
524
|
hour = datetime.datetime.now().hour
|
|
437
525
|
if 5 <= hour < 12:
|
|
438
526
|
period = "morning"
|
|
@@ -444,13 +532,18 @@ class TimeLayer(Layer):
|
|
|
444
532
|
tag = f"[time:{period}]"
|
|
445
533
|
ms = int((time.time() - start) * 1000)
|
|
446
534
|
return LayerResult(
|
|
447
|
-
layer_id=self.id,
|
|
448
|
-
|
|
535
|
+
layer_id=self.id,
|
|
536
|
+
tag=tag,
|
|
537
|
+
content=period,
|
|
538
|
+
tokens_est=1,
|
|
539
|
+
compute_ms=ms,
|
|
540
|
+
cached=False,
|
|
449
541
|
)
|
|
450
542
|
|
|
451
543
|
|
|
452
544
|
# --- L3.5: Knowledge Retrieval ---
|
|
453
545
|
|
|
546
|
+
|
|
454
547
|
class KnowledgeRetrievalLayer(Layer):
|
|
455
548
|
"""L3.5: Semantic knowledge retrieval from vector DB.
|
|
456
549
|
|
|
@@ -459,7 +552,9 @@ class KnowledgeRetrievalLayer(Layer):
|
|
|
459
552
|
is unavailable or empty.
|
|
460
553
|
"""
|
|
461
554
|
|
|
462
|
-
def __init__(
|
|
555
|
+
def __init__(
|
|
556
|
+
self, vector_store: Any = None, max_chunks: int = 3, max_tokens: int = 400
|
|
557
|
+
) -> None:
|
|
463
558
|
self._store = vector_store
|
|
464
559
|
self._max_chunks = max_chunks
|
|
465
560
|
self._max_tokens = max_tokens
|
|
@@ -485,26 +580,50 @@ class KnowledgeRetrievalLayer(Layer):
|
|
|
485
580
|
|
|
486
581
|
if not self._store or not ctx.user_input:
|
|
487
582
|
return LayerResult(
|
|
488
|
-
layer_id=self.id,
|
|
489
|
-
|
|
583
|
+
layer_id=self.id,
|
|
584
|
+
tag="",
|
|
585
|
+
content="",
|
|
586
|
+
tokens_est=0,
|
|
587
|
+
compute_ms=0,
|
|
588
|
+
cached=False,
|
|
490
589
|
)
|
|
491
590
|
|
|
492
591
|
try:
|
|
493
592
|
results = self._store.search(ctx.user_input, top_k=self._max_chunks)
|
|
494
593
|
except Exception:
|
|
495
594
|
return LayerResult(
|
|
496
|
-
layer_id=self.id,
|
|
497
|
-
|
|
595
|
+
layer_id=self.id,
|
|
596
|
+
tag="",
|
|
597
|
+
content="",
|
|
598
|
+
tokens_est=0,
|
|
599
|
+
compute_ms=0,
|
|
600
|
+
cached=False,
|
|
498
601
|
)
|
|
499
602
|
|
|
500
603
|
if not results:
|
|
501
604
|
ms = int((time.time() - start) * 1000)
|
|
502
605
|
return LayerResult(
|
|
503
|
-
layer_id=self.id,
|
|
504
|
-
|
|
606
|
+
layer_id=self.id,
|
|
607
|
+
tag="",
|
|
608
|
+
content="",
|
|
609
|
+
tokens_est=0,
|
|
610
|
+
compute_ms=ms,
|
|
611
|
+
cached=False,
|
|
505
612
|
)
|
|
506
613
|
|
|
507
|
-
|
|
614
|
+
session_id = ctx.extra.get("session_id", "default") if ctx.extra else "default"
|
|
615
|
+
project_path = ctx.cwd or None
|
|
616
|
+
|
|
617
|
+
overlapping: list[dict] = []
|
|
618
|
+
try:
|
|
619
|
+
from core.synapse.kb_cache import KBSessionCache
|
|
620
|
+
|
|
621
|
+
cache = KBSessionCache(session_id=session_id, project_path=project_path)
|
|
622
|
+
cache.store(ctx.user_input, results)
|
|
623
|
+
overlapping = cache.get_overlap(ctx.user_input, threshold=0.3)
|
|
624
|
+
except Exception:
|
|
625
|
+
pass
|
|
626
|
+
|
|
508
627
|
snippets = []
|
|
509
628
|
total_tokens = 0
|
|
510
629
|
for r in results:
|
|
@@ -517,25 +636,44 @@ class KnowledgeRetrievalLayer(Layer):
|
|
|
517
636
|
snippets.append(snippet)
|
|
518
637
|
total_tokens += tokens
|
|
519
638
|
|
|
520
|
-
if not snippets:
|
|
639
|
+
if not snippets and not overlapping:
|
|
521
640
|
ms = int((time.time() - start) * 1000)
|
|
522
641
|
return LayerResult(
|
|
523
|
-
layer_id=self.id,
|
|
524
|
-
|
|
642
|
+
layer_id=self.id,
|
|
643
|
+
tag="",
|
|
644
|
+
content="",
|
|
645
|
+
tokens_est=0,
|
|
646
|
+
compute_ms=ms,
|
|
647
|
+
cached=False,
|
|
525
648
|
)
|
|
526
649
|
|
|
527
|
-
|
|
528
|
-
|
|
650
|
+
parts: list[str] = []
|
|
651
|
+
if overlapping:
|
|
652
|
+
for o in overlapping[:2]:
|
|
653
|
+
text = o.get("text", "")[:200].replace("\n", " ").strip()
|
|
654
|
+
src = o.get("source", "").split("/")[-1] if o.get("source") else ""
|
|
655
|
+
parts.append(f"[cached] {src}: {text}" if src else f"[cached] {text}")
|
|
656
|
+
parts.extend(snippets)
|
|
657
|
+
|
|
658
|
+
content = " | ".join(parts)
|
|
659
|
+
chunk_count = len(snippets) + (len(overlapping) if overlapping else 0)
|
|
660
|
+
tag = f"[knowledge:{chunk_count} chunks]"
|
|
529
661
|
ms = int((time.time() - start) * 1000)
|
|
662
|
+
tokens_est = len(content.split())
|
|
530
663
|
|
|
531
664
|
return LayerResult(
|
|
532
|
-
layer_id=self.id,
|
|
533
|
-
|
|
665
|
+
layer_id=self.id,
|
|
666
|
+
tag=tag,
|
|
667
|
+
content=content,
|
|
668
|
+
tokens_est=tokens_est,
|
|
669
|
+
compute_ms=ms,
|
|
670
|
+
cached=False,
|
|
534
671
|
)
|
|
535
672
|
|
|
536
673
|
|
|
537
674
|
# --- L8: Forge Context ---
|
|
538
675
|
|
|
676
|
+
|
|
539
677
|
class ForgeContextLayer(Layer):
|
|
540
678
|
"""L8: Active forge plan context — decisions, risks, rejected approaches."""
|
|
541
679
|
|
|
@@ -559,11 +697,14 @@ class ForgeContextLayer(Layer):
|
|
|
559
697
|
start = time.time()
|
|
560
698
|
try:
|
|
561
699
|
from core.forge.persistence import get_active_plan
|
|
700
|
+
|
|
562
701
|
plan = get_active_plan()
|
|
563
702
|
except Exception:
|
|
564
703
|
plan = None
|
|
565
704
|
if plan is None:
|
|
566
|
-
return LayerResult(
|
|
705
|
+
return LayerResult(
|
|
706
|
+
layer_id=self.id, tag="", content="", tokens_est=0, compute_ms=0, cached=False
|
|
707
|
+
)
|
|
567
708
|
tag = f"[forge:{plan.id}]"
|
|
568
709
|
parts = [f"Forge plan: {plan.id} ({plan.status.value})"]
|
|
569
710
|
if plan.critic.confidence > 0:
|
|
@@ -580,4 +721,66 @@ class ForgeContextLayer(Layer):
|
|
|
580
721
|
parts.append(f"Risks: {'; '.join(risks[:3])}")
|
|
581
722
|
content = " | ".join(parts)
|
|
582
723
|
ms = int((time.time() - start) * 1000)
|
|
583
|
-
return LayerResult(
|
|
724
|
+
return LayerResult(
|
|
725
|
+
layer_id=self.id,
|
|
726
|
+
tag=tag,
|
|
727
|
+
content=content,
|
|
728
|
+
tokens_est=len(content.split()),
|
|
729
|
+
compute_ms=ms,
|
|
730
|
+
cached=False,
|
|
731
|
+
)
|
|
732
|
+
|
|
733
|
+
|
|
734
|
+
# --- L9: Session Memory Context ---
|
|
735
|
+
|
|
736
|
+
|
|
737
|
+
class SessionContextLayer(Layer):
|
|
738
|
+
"""L9: Restored session context from memory store.
|
|
739
|
+
|
|
740
|
+
Provides context from previous sessions via build_resume_context().
|
|
741
|
+
Shows workflow position, pending items, and violations.
|
|
742
|
+
"""
|
|
743
|
+
|
|
744
|
+
@property
|
|
745
|
+
def id(self) -> str:
|
|
746
|
+
return "L9"
|
|
747
|
+
|
|
748
|
+
@property
|
|
749
|
+
def name(self) -> str:
|
|
750
|
+
return "SessionMemory"
|
|
751
|
+
|
|
752
|
+
@property
|
|
753
|
+
def cache_ttl(self) -> int:
|
|
754
|
+
return 0
|
|
755
|
+
|
|
756
|
+
@property
|
|
757
|
+
def priority(self) -> int:
|
|
758
|
+
return 90
|
|
759
|
+
|
|
760
|
+
def compute(self, ctx: PromptContext) -> LayerResult:
|
|
761
|
+
start = time.time()
|
|
762
|
+
try:
|
|
763
|
+
from core.memory.rehydrator import build_resume_context
|
|
764
|
+
|
|
765
|
+
content = build_resume_context()
|
|
766
|
+
except Exception:
|
|
767
|
+
content = ""
|
|
768
|
+
|
|
769
|
+
if not content:
|
|
770
|
+
return LayerResult(
|
|
771
|
+
layer_id=self.id, tag="", content="", tokens_est=0, compute_ms=0, cached=False
|
|
772
|
+
)
|
|
773
|
+
|
|
774
|
+
lines = content.split("\n")
|
|
775
|
+
tag = "[session:resume]"
|
|
776
|
+
tokens = len(content.split())
|
|
777
|
+
|
|
778
|
+
ms = int((time.time() - start) * 1000)
|
|
779
|
+
return LayerResult(
|
|
780
|
+
layer_id=self.id,
|
|
781
|
+
tag=tag,
|
|
782
|
+
content=content,
|
|
783
|
+
tokens_est=tokens,
|
|
784
|
+
compute_ms=ms,
|
|
785
|
+
cached=False,
|
|
786
|
+
)
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|