arkaos 4.0.2 → 4.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/README.md +2 -2
- package/VERSION +1 -1
- package/arka/SKILL.md +16 -22
- package/arka/skills/flow/SKILL.md +108 -234
- package/config/claude-agents/eduardo-copy.md +53 -0
- package/config/claude-agents/francisca-tech.md +52 -0
- package/config/claude-agents/marta-cqo.md +52 -0
- package/config/claude-agents/paulo-tech-lead.md +50 -0
- package/config/cognition/schedules.yaml +15 -0
- package/config/constitution.yaml +17 -21
- package/config/hooks/_lib/workflow-classifier.sh +1 -1
- package/config/hooks/post-tool-use.sh +40 -539
- package/config/hooks/pre-tool-use.sh +32 -281
- package/config/hooks/session-start.sh +8 -10
- package/config/hooks/stop.sh +30 -425
- package/config/hooks/user-prompt-submit.sh +42 -459
- package/core/cognition/__pycache__/dreaming.cpython-313.pyc +0 -0
- package/core/cognition/dreaming.py +5 -0
- package/core/cognition/scheduler/__pycache__/daemon.cpython-313.pyc +0 -0
- package/core/cognition/scheduler/daemon.py +10 -0
- package/core/forge/__init__.py +2 -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__/persistence.cpython-313.pyc +0 -0
- package/core/forge/__pycache__/renderer.cpython-313.pyc +0 -0
- package/core/forge/__pycache__/runtime_dispatcher.cpython-313.pyc +0 -0
- package/core/forge/__pycache__/schema.cpython-313.pyc +0 -0
- package/core/forge/orchestrator.py +50 -27
- package/core/forge/persistence.py +16 -0
- package/core/forge/renderer.py +16 -0
- package/core/forge/runtime_dispatcher.py +34 -19
- package/core/forge/schema.py +12 -0
- package/core/governance/__pycache__/__init__.cpython-312.pyc +0 -0
- package/core/governance/__pycache__/closing_marker_check.cpython-313.pyc +0 -0
- package/core/governance/__pycache__/compliance_telemetry.cpython-313.pyc +0 -0
- package/core/governance/__pycache__/constitution.cpython-312.pyc +0 -0
- package/core/governance/__pycache__/evidence_checks.cpython-313.pyc +0 -0
- package/core/governance/__pycache__/meta_tag_check.cpython-313.pyc +0 -0
- package/core/governance/__pycache__/qg_verdict.cpython-313.pyc +0 -0
- package/core/governance/__pycache__/review_workflow.cpython-313.pyc +0 -0
- package/core/governance/__pycache__/skill_proposer.cpython-313.pyc +0 -0
- package/core/governance/closing_marker_check.py +9 -5
- package/core/governance/compliance_telemetry.py +1 -1
- package/core/governance/compliance_telemetry_cli.py +1 -1
- package/core/governance/evidence_checks.py +421 -0
- package/core/governance/meta_tag_check.py +36 -0
- package/core/governance/qg_verdict.py +78 -0
- package/core/governance/review_workflow.py +34 -1
- package/core/governance/skill_proposer.py +2 -1
- package/core/hooks/__init__.py +12 -0
- package/core/hooks/__pycache__/__init__.cpython-312.pyc +0 -0
- package/core/hooks/__pycache__/__init__.cpython-313.pyc +0 -0
- package/core/hooks/__pycache__/_shared.cpython-312.pyc +0 -0
- package/core/hooks/__pycache__/_shared.cpython-313.pyc +0 -0
- package/core/hooks/__pycache__/post_tool_use.cpython-312.pyc +0 -0
- package/core/hooks/__pycache__/post_tool_use.cpython-313.pyc +0 -0
- package/core/hooks/__pycache__/pre_tool_use.cpython-312.pyc +0 -0
- package/core/hooks/__pycache__/pre_tool_use.cpython-313.pyc +0 -0
- package/core/hooks/__pycache__/stop.cpython-312.pyc +0 -0
- package/core/hooks/__pycache__/stop.cpython-313.pyc +0 -0
- package/core/hooks/__pycache__/user_prompt_submit.cpython-312.pyc +0 -0
- package/core/hooks/__pycache__/user_prompt_submit.cpython-313.pyc +0 -0
- package/core/hooks/_shared.py +110 -0
- package/core/hooks/post_tool_use.py +689 -0
- package/core/hooks/pre_tool_use.py +267 -0
- package/core/hooks/stop.py +395 -0
- package/core/hooks/user_prompt_submit.py +600 -0
- package/core/jobs/__pycache__/__init__.cpython-312.pyc +0 -0
- package/core/jobs/__pycache__/auto_doc_worker.cpython-312.pyc +0 -0
- package/core/jobs/__pycache__/manager.cpython-312.pyc +0 -0
- package/core/knowledge/__pycache__/embedder.cpython-312.pyc +0 -0
- package/core/knowledge/__pycache__/embedder.cpython-313.pyc +0 -0
- package/core/knowledge/__pycache__/vector_store.cpython-312.pyc +0 -0
- package/core/knowledge/__pycache__/vector_store.cpython-313.pyc +0 -0
- package/core/knowledge/embedder.py +118 -11
- package/core/knowledge/vector_store.py +111 -14
- package/core/runtime/__pycache__/__init__.cpython-312.pyc +0 -0
- package/core/runtime/__pycache__/__init__.cpython-313.pyc +0 -0
- package/core/runtime/__pycache__/base.cpython-312.pyc +0 -0
- package/core/runtime/__pycache__/base.cpython-313.pyc +0 -0
- package/core/runtime/__pycache__/capabilities_cli.cpython-312.pyc +0 -0
- package/core/runtime/__pycache__/capabilities_cli.cpython-313.pyc +0 -0
- package/core/runtime/__pycache__/claude_code.cpython-312.pyc +0 -0
- package/core/runtime/__pycache__/claude_code.cpython-313.pyc +0 -0
- package/core/runtime/__pycache__/codex_cli.cpython-312.pyc +0 -0
- package/core/runtime/__pycache__/codex_cli.cpython-313.pyc +0 -0
- package/core/runtime/__pycache__/cost_governor.cpython-312.pyc +0 -0
- package/core/runtime/__pycache__/cost_governor.cpython-313.pyc +0 -0
- package/core/runtime/__pycache__/cursor.cpython-312.pyc +0 -0
- package/core/runtime/__pycache__/cursor.cpython-313.pyc +0 -0
- package/core/runtime/__pycache__/gemini_cli.cpython-312.pyc +0 -0
- package/core/runtime/__pycache__/gemini_cli.cpython-313.pyc +0 -0
- package/core/runtime/__pycache__/llm_provider.cpython-312.pyc +0 -0
- package/core/runtime/__pycache__/llm_provider.cpython-313.pyc +0 -0
- package/core/runtime/__pycache__/llm_retry.cpython-312.pyc +0 -0
- package/core/runtime/__pycache__/llm_retry.cpython-313.pyc +0 -0
- package/core/runtime/__pycache__/native_usage.cpython-312.pyc +0 -0
- package/core/runtime/__pycache__/native_usage.cpython-313.pyc +0 -0
- package/core/runtime/__pycache__/path_resolver.cpython-312.pyc +0 -0
- package/core/runtime/__pycache__/pricing.cpython-312.pyc +0 -0
- package/core/runtime/__pycache__/pricing.cpython-313.pyc +0 -0
- package/core/runtime/__pycache__/registry.cpython-312.pyc +0 -0
- package/core/runtime/__pycache__/registry.cpython-313.pyc +0 -0
- package/core/runtime/__pycache__/squad_orchestrator.cpython-312.pyc +0 -0
- package/core/runtime/base.py +29 -0
- package/core/runtime/capabilities_cli.py +74 -0
- package/core/runtime/claude_code.py +8 -0
- package/core/runtime/codex_cli.py +205 -37
- package/core/runtime/cost_governor.py +184 -0
- package/core/runtime/cursor.py +8 -0
- package/core/runtime/gemini_cli.py +8 -0
- package/core/runtime/llm_provider.py +32 -13
- package/core/runtime/llm_retry.py +233 -0
- package/core/runtime/native_usage.py +186 -0
- package/core/runtime/pricing.py +9 -0
- package/core/runtime/registry.py +3 -0
- package/core/shared/__pycache__/test_evidence.cpython-313-pytest-9.1.1.pyc +0 -0
- package/core/shared/__pycache__/test_evidence.cpython-313.pyc +0 -0
- package/core/shared/test_evidence.py +34 -0
- package/core/synapse/__pycache__/agent_experiences_layer.cpython-312.pyc +0 -0
- package/core/synapse/__pycache__/engine.cpython-312.pyc +0 -0
- package/core/synapse/__pycache__/engine.cpython-313.pyc +0 -0
- package/core/synapse/__pycache__/graph_context_layer.cpython-313.pyc +0 -0
- package/core/synapse/__pycache__/kb_cache.cpython-312.pyc +0 -0
- package/core/synapse/__pycache__/kb_cache.cpython-313.pyc +0 -0
- package/core/synapse/__pycache__/layers.cpython-312.pyc +0 -0
- package/core/synapse/__pycache__/layers.cpython-313.pyc +0 -0
- package/core/synapse/engine.py +7 -0
- package/core/synapse/graph_context_layer.py +212 -0
- package/core/synapse/kb_cache.py +3 -0
- package/core/synapse/layers.py +104 -20
- package/core/workflow/__pycache__/enforcer.cpython-313.pyc +0 -0
- package/core/workflow/__pycache__/flow_enforcer.cpython-312.pyc +0 -0
- package/core/workflow/__pycache__/flow_enforcer.cpython-313.pyc +0 -0
- package/core/workflow/__pycache__/gate_checkpoint.cpython-312.pyc +0 -0
- package/core/workflow/__pycache__/gate_checkpoint.cpython-313.pyc +0 -0
- package/core/workflow/__pycache__/research_gate.cpython-313.pyc +0 -0
- package/core/workflow/__pycache__/rules_registry.cpython-313.pyc +0 -0
- package/core/workflow/__pycache__/specialist_enforcer.cpython-312.pyc +0 -0
- package/core/workflow/__pycache__/specialist_enforcer.cpython-313.pyc +0 -0
- package/core/workflow/enforcer.py +5 -16
- package/core/workflow/flow_enforcer.py +36 -11
- package/core/workflow/gate_checkpoint.py +149 -0
- package/core/workflow/research_gate.py +15 -3
- package/core/workflow/rules_registry.py +137 -389
- package/core/workflow/specialist_enforcer.py +21 -4
- package/dashboard/app/pages/knowledge/index.vue +4 -1
- package/departments/ops/skills/n8n-flow/SKILL.md +13 -0
- package/departments/ops/skills/workflow-automate/SKILL.md +16 -0
- package/departments/quality/SKILL.md +45 -5
- package/departments/quality/agents/copy-director.yaml +3 -1
- package/departments/quality/agents/tech-director.yaml +3 -1
- package/installer/adapters/claude-code.js +46 -2
- package/installer/config-seed.js +39 -14
- package/installer/doctor.js +8 -0
- package/installer/graphify.js +153 -0
- package/installer/index.js +20 -0
- package/installer/init.js +11 -0
- package/installer/update.js +19 -0
- package/package.json +1 -1
- package/pyproject.toml +1 -1
- package/scripts/__pycache__/dashboard-api.cpython-313.pyc +0 -0
- package/scripts/__pycache__/knowledge-index.cpython-313.pyc +0 -0
- package/scripts/__pycache__/synapse-bridge.cpython-312.pyc +0 -0
- package/scripts/__pycache__/synapse-bridge.cpython-313.pyc +0 -0
- package/scripts/knowledge-index.py +8 -2
- package/scripts/synapse-bridge.py +102 -26
|
@@ -0,0 +1,149 @@
|
|
|
1
|
+
"""Gate checkpoint persistence for the 4-gate evidence flow (v4.1.0).
|
|
2
|
+
|
|
3
|
+
Invoked by the Stop hook once per assistant turn. Scans recent assistant
|
|
4
|
+
messages for `[arka:gate:N]` markers and persists the furthest gate
|
|
5
|
+
observed to BOTH stores:
|
|
6
|
+
|
|
7
|
+
- ``~/.arkaos/workflow-state.json`` via :mod:`core.workflow.state`
|
|
8
|
+
- ``~/.arkaos/sessions/<id>/workflow-state.json`` via
|
|
9
|
+
:class:`core.memory.session_store.SessionStore` (consumed by
|
|
10
|
+
:mod:`core.memory.rehydrator` at SessionStart)
|
|
11
|
+
|
|
12
|
+
This is what makes a rate-limit or context-exhaustion interruption
|
|
13
|
+
resumable at the correct gate instead of restarting the flow. See ADR
|
|
14
|
+
``docs/adr/2026-07-04-evidence-flow.md``.
|
|
15
|
+
|
|
16
|
+
Never raises: checkpointing must not break a Stop hook.
|
|
17
|
+
"""
|
|
18
|
+
|
|
19
|
+
import json
|
|
20
|
+
import re
|
|
21
|
+
import sys
|
|
22
|
+
|
|
23
|
+
from core.memory.session_store import SessionStore, WorkflowSnapshot
|
|
24
|
+
from core.shared.safe_session_id import safe_session_id
|
|
25
|
+
from core.workflow import state
|
|
26
|
+
from core.workflow.flow_enforcer import _load_last_assistant_messages
|
|
27
|
+
|
|
28
|
+
GATES: tuple[str, ...] = (
|
|
29
|
+
"gate-1-context",
|
|
30
|
+
"gate-2-plan",
|
|
31
|
+
"gate-3-execute",
|
|
32
|
+
"gate-4-review",
|
|
33
|
+
)
|
|
34
|
+
|
|
35
|
+
GATE_MARKER_RE = re.compile(r"\[arka:gate:([1-4])\]", re.IGNORECASE)
|
|
36
|
+
GATE3_EVIDENCE_RE = re.compile(
|
|
37
|
+
r"\[arka:gate:3\]\s*evidence:\s*(?P<evidence>.+)", re.IGNORECASE
|
|
38
|
+
)
|
|
39
|
+
|
|
40
|
+
_SCAN_WINDOW = 20
|
|
41
|
+
|
|
42
|
+
|
|
43
|
+
def extract_latest_gate(messages: list[str]) -> int | None:
|
|
44
|
+
"""Return the highest gate number observed across the messages."""
|
|
45
|
+
latest: int | None = None
|
|
46
|
+
for text in messages:
|
|
47
|
+
for match in GATE_MARKER_RE.finditer(text):
|
|
48
|
+
number = int(match.group(1))
|
|
49
|
+
if latest is None or number > latest:
|
|
50
|
+
latest = number
|
|
51
|
+
return latest
|
|
52
|
+
|
|
53
|
+
|
|
54
|
+
def extract_gate3_evidence(messages: list[str]) -> str | None:
|
|
55
|
+
"""Return the most recent Gate-3 evidence line, if any."""
|
|
56
|
+
evidence: str | None = None
|
|
57
|
+
for text in messages:
|
|
58
|
+
for match in GATE3_EVIDENCE_RE.finditer(text):
|
|
59
|
+
evidence = match.group("evidence").strip()
|
|
60
|
+
return evidence
|
|
61
|
+
|
|
62
|
+
|
|
63
|
+
def _phase_statuses(latest: int) -> dict[str, str]:
|
|
64
|
+
"""Map each gate to completed/in_progress/pending given the latest gate."""
|
|
65
|
+
statuses: dict[str, str] = {}
|
|
66
|
+
for index, gate in enumerate(GATES, start=1):
|
|
67
|
+
if index < latest:
|
|
68
|
+
statuses[gate] = "completed"
|
|
69
|
+
elif index == latest:
|
|
70
|
+
statuses[gate] = "in_progress"
|
|
71
|
+
else:
|
|
72
|
+
statuses[gate] = "pending"
|
|
73
|
+
return statuses
|
|
74
|
+
|
|
75
|
+
|
|
76
|
+
def _persist_global_state(latest: int, project: str, evidence: str | None) -> None:
|
|
77
|
+
current = state.get_state()
|
|
78
|
+
if current is None or current.get("workflow") != "evidence-flow":
|
|
79
|
+
state.init_workflow("evidence-flow", project, list(GATES))
|
|
80
|
+
for gate, status in _phase_statuses(latest).items():
|
|
81
|
+
artifact = None
|
|
82
|
+
if gate == GATES[2] and evidence:
|
|
83
|
+
artifact = evidence
|
|
84
|
+
state.update_phase(gate, status, artifact=artifact)
|
|
85
|
+
|
|
86
|
+
|
|
87
|
+
def _persist_session_snapshot(
|
|
88
|
+
session_id: str, latest: int, evidence: str | None
|
|
89
|
+
) -> None:
|
|
90
|
+
safe = safe_session_id(session_id)
|
|
91
|
+
if safe is None:
|
|
92
|
+
return
|
|
93
|
+
store = SessionStore(safe)
|
|
94
|
+
snapshot = WorkflowSnapshot(
|
|
95
|
+
workflow_id=safe,
|
|
96
|
+
workflow_name="evidence-flow",
|
|
97
|
+
current_phase=GATES[latest - 1],
|
|
98
|
+
phases=_phase_statuses(latest),
|
|
99
|
+
artifacts=[evidence] if evidence else [],
|
|
100
|
+
)
|
|
101
|
+
store.save_workflow_snapshot(snapshot)
|
|
102
|
+
|
|
103
|
+
|
|
104
|
+
def checkpoint(
|
|
105
|
+
transcript_path: str,
|
|
106
|
+
session_id: str,
|
|
107
|
+
project: str = "",
|
|
108
|
+
messages: list[str] | None = None,
|
|
109
|
+
) -> dict | None:
|
|
110
|
+
"""Persist the furthest observed gate. Returns a summary dict or None.
|
|
111
|
+
|
|
112
|
+
``messages`` (PR-6 hook consolidation): pre-parsed assistant messages;
|
|
113
|
+
when None the transcript is read from ``transcript_path``.
|
|
114
|
+
"""
|
|
115
|
+
try:
|
|
116
|
+
if messages is None:
|
|
117
|
+
messages = _load_last_assistant_messages(
|
|
118
|
+
transcript_path, _SCAN_WINDOW
|
|
119
|
+
)
|
|
120
|
+
else:
|
|
121
|
+
messages = messages[-_SCAN_WINDOW:]
|
|
122
|
+
latest = extract_latest_gate(messages)
|
|
123
|
+
if latest is None:
|
|
124
|
+
return None
|
|
125
|
+
evidence = extract_gate3_evidence(messages)
|
|
126
|
+
_persist_global_state(latest, project, evidence)
|
|
127
|
+
_persist_session_snapshot(session_id, latest, evidence)
|
|
128
|
+
return {
|
|
129
|
+
"gate": latest,
|
|
130
|
+
"current_phase": GATES[latest - 1],
|
|
131
|
+
"evidence": evidence,
|
|
132
|
+
}
|
|
133
|
+
except Exception: # noqa: BLE001 — a Stop hook must never break the turn
|
|
134
|
+
return None
|
|
135
|
+
|
|
136
|
+
|
|
137
|
+
def main(argv: list[str]) -> int:
|
|
138
|
+
"""CLI: gate_checkpoint <transcript_path> <session_id> [project]."""
|
|
139
|
+
if len(argv) < 2:
|
|
140
|
+
return 0
|
|
141
|
+
project = argv[2] if len(argv) > 2 else ""
|
|
142
|
+
result = checkpoint(argv[0], argv[1], project)
|
|
143
|
+
if result is not None:
|
|
144
|
+
print(json.dumps(result))
|
|
145
|
+
return 0
|
|
146
|
+
|
|
147
|
+
|
|
148
|
+
if __name__ == "__main__":
|
|
149
|
+
raise SystemExit(main(sys.argv[1:]))
|
|
@@ -2,7 +2,8 @@
|
|
|
2
2
|
|
|
3
3
|
Intercepts calls to Context7, WebSearch, WebFetch and Firecrawl MCP tools.
|
|
4
4
|
Runs AFTER flow_enforcer in the PreToolUse hook chain. Independent gate:
|
|
5
|
-
both must pass. Feature-flagged behind `hooks.kbFirst` (default
|
|
5
|
+
both must pass. Feature-flagged behind `hooks.kbFirst` (default TRUE
|
|
6
|
+
since v4.1 — see `_feature_flag_on`).
|
|
6
7
|
|
|
7
8
|
Behaviour contract (plan 2026-04-20-intelligence-v2, §Épico B):
|
|
8
9
|
1. Tool not in RESEARCH_EXTERNAL_TOOLS → allow.
|
|
@@ -106,13 +107,24 @@ _safe_session_id = _safe_session_id_module.safe_session_id
|
|
|
106
107
|
|
|
107
108
|
|
|
108
109
|
def _feature_flag_on() -> bool:
|
|
110
|
+
"""Resolve the `hooks.kbFirst` flag.
|
|
111
|
+
|
|
112
|
+
Default-when-missing semantics (operator decision, PR-3 v4.1):
|
|
113
|
+
KB-first is ON out of the box. The installer template
|
|
114
|
+
(installer/config-seed.js) seeds `hooks.kbFirst = true`, so a missing
|
|
115
|
+
config file or a missing key now defaults to TRUE — matching the
|
|
116
|
+
seeded template instead of contradicting it. An explicit
|
|
117
|
+
`"kbFirst": false` in the user's config always wins. A corrupt/
|
|
118
|
+
unreadable config returns False (fail-open): the gate must never
|
|
119
|
+
deny tool calls based on intent it could not read.
|
|
120
|
+
"""
|
|
109
121
|
if not CONFIG_PATH.exists():
|
|
110
|
-
return
|
|
122
|
+
return True
|
|
111
123
|
try:
|
|
112
124
|
data = json.loads(CONFIG_PATH.read_text(encoding="utf-8"))
|
|
113
125
|
except (json.JSONDecodeError, OSError):
|
|
114
126
|
return False
|
|
115
|
-
return bool(data.get("hooks", {}).get("kbFirst",
|
|
127
|
+
return bool(data.get("hooks", {}).get("kbFirst", True))
|
|
116
128
|
|
|
117
129
|
|
|
118
130
|
def _bypass_env_active() -> bool:
|